|
|
Запросы 4GL
Written by © Serguey Klimoff, 2003, Russia
Данная статья является выжимкой материалов The Engine Crew Monographs
Содержание
Архитектура БД
Файлы и блоки БД
Адресация блоков
База данных в единичном файле
БД, состоящая из нескольких файлов
Как используется пространство
Размещение блоков
Запись RM блоков
Адресация записи
Создание записи
Обновление записи
Удаление записи
Индексные блоки
Определение пространства, используемого БД
Запросы
4gl queries
Курсоры индексов
Брэкеты индекса (index brackets)
Использование одного брэкета индекса для извлечения записи
Использование более одного брэкета индекса для извлечения записи
Выбор записей
Запросы по слову (word indexes)
Поиск по ROWID
Поиск уникальной записи через FIND
Объединение
Списки полей
Предварительная выборка записей в сетевых сообщениях
Список результатов
Сортировка результатов запроса
Позиционирование в запросе
Кэш запроса
Стратегия выполнения
Характеристики запросов FOR EACH
Характеристики PRESELECT запросов
Характеристика FIND запросов
Характеристики GET запросов
Когда какой запрос лучше использовать
Архитектура БД
Файлы и блоки БД
Данные в БД PROGRESS могут храниться как в единичном, так и в нескольких файлах. БД, состоящая из одного файла может быть преобразована в БД из нескольких файлов.
БД PROGRESS разбита на участки определенного размера, называемые блоки базы данных, или страницы базы данных. Все дисковые операции ввода/вывода в БД осуществляются посредством блоков, состоящих из одной или нескольких страниц. До появления PROGRESS v.8.2 размер блоков БД был фиксированный, но варьировался в рамках различных операционных систем. В версии 8.2 и более поздних размер блоков может быть выбран из одной из следующих величин 1024, 2048, 4096, or 8192 байт при создании БД.
В каждом блоке имеется стандартный 16-битный заголовок, содержащий следующую информацию
Существует несколько различных типов блоков. Каждый тип используется для хранения различных данных.
Тип блока
|
Содержит |
1 – Мастер блок |
Статус БД и др. информацию |
2 – Индексный блок |
Данные о введенных индексах |
3 – Блок записи |
Записи |
4 – Свободный блок |
Свободное пространство (ранее использованное) |
5 – Индексный анкерный блок |
Указатели индексов коренных блоков |
6 – Порядковый блок |
Величины последовательности |
7 – Пустой блок |
Пустое пространство (никогда не используемое) |
Адресация блоков
Каждый блок БД снабжен уникальным 32-битным идентификатором, называемым ключом БД (dbkey), указывающим на конкретное место блока в БД. Блоки нумеруются последовательно, начиная с 1. Для вычисления ключа БД нужно умножить номер блока на 32 (64, если размер блока равен 8192).
Если размер блока БД меньше 8192 байт, то поле номера блока ключа БД составляет 26 бит, позволяя БД содержать до 67.107.839 блоков.
Если размер блока БД равен 8192 байт, тот поле номера блока ключа БД составляет 25 бит, позволяя БД содержать до 33.554.431 блоков.
Формат ключа БД в сочетании с размером блока определяют максимально возможный размер БД.
Размер блока БД (байты)
|
Максимальный размер БД (байты)
|
1024 |
68,719,475,712 |
2048 |
137,438,951,424 |
4096 |
274,877,902,848 |
8192 |
274,877,898,752 |
База данных в единичном файле
Когда БД представляет собой единичный файл, все блоки БД содержатся в одном файле, называемом <something>.db, где <something> имя БД. Есть еще несколько файлов, являющихся частью БД. К ним относятся следующие файлы:
<something>.db (единичный файл, содержащий данные)
<something>.bi (единичный файл, содержащий BI лог)
<something>.ai (единичный файл, содержащий необязательный AI лог)
<something>.lg (файл, содержащий startup, login, error, and shutdown messages)
Первый блок .db файла служит первым блоком БД, и остальные блоки следуют за ним по порядку до последнего блока. По мере пополнения БД новыми данными .db файл будет увеличиваться. БД, состоящая из единичного файла может увеличиваться до тех пор, пока файл данных не достигнет 2 Гб.
БД, состоящая из нескольких файлов
Мульти-файловая БД состоит из файла с именем <something>.db, где <something> имя БД, множества файлов данных, имеющих имена в виде <something.dnn>. Как и в базах данных, состоящих из одного файла, в мульти-файловой БД также имеются ряд других, являющихся частью БД.
<something>.db file содержит заголовок с информацией о базе данных и список имен и месторасположения всех других файлов, входящих в БД.
<something>.db (содержит перечень всех прочих файлов и информацию об их состоянии)
<something>.dnn (содержат данные)
<something>.bnn (содержат требуемый исходный лог)
<something>.ann (содержат необязательный преобразованный лог)
<something>.lg (текстовой файл, содержащий startup, login, error, and shutdown messages)
<something>.st (текстовой файл, содержащий описание структуры БД, которое использовалось при ее создании)
Когда БД хранится в нескольких файлах, каждый файл называется экстент (extent). За исключением последнего, все экстенты имеют фиксированный размер. Каждый экстент имеет заголовок, не содержащий данных (и который не учитывается при подсчете блоков) и некоторое количество блоков БД. Блоки нумеруются последовательно через экстенты. Первый экстент содержит блоки 1 through n1, the second blocks n1+1 through n1+n2, the third blocks n1+n2+1 through n1+n2+n3, и т.д. (где n1, n2, and n3 количество блоков в экстентах 1, 2, and 3 соответственно).
Мульти-файловая БД имеет следующие преимущества перед БД, состоящий из единичного файла:
- Ваша БД может превосходить по размеру 2 Гб, даже если отдельные файлы БД этого не могут.
- Вы можете разместить вашу БД более, чем на одном диске.
- Вы можете разместить транзакционные логи более, чем на одном диске и их расположение будет запоминаться автоматически.
Как используется пространство
Не смотря на то, что размер БД может превосходит предел 2ГБ, размер отдельного файла БД этот предел превосходить не может. Связано это прежде всего с работой функции lseek, которая позволяет осуществлять случайный доступ внутри файла. Смещение представляет собой 32-битную величину. Лимит в 2ГБ распространяется на все реализации PROGRESS, включая доступ из 4GL программ, даже если отдельные ОС могут оперировать с файлами больших размеров.
Размещение блоков
Когда серверу БД необходим блок для хранения чего-либо, во-первых, он проверяет список неиспользуемых блоков, называемый свободной цепочкой (free chain). Если свободная цепочка содержит хотя бы один блок, то он будет использован. Свободные блоки - это блоки использованные ранее (всегда индексные). Если же свободная цепочка исчерпана, то БД логически расширяется и использует блоки, которые ранее не были использованы, эти блоки называются пустыми блоками. Пустые блоки всегда размещаются в конце файла БД, на начало их размещения указывает специальный индекс high-water mark. В случае использования пустого блока, значение индекса увеличивается, блок форматируется и заполняется. Если блок освобождается, то он не становится пустым, а становится свободным и помещается в свободную цепочку.
Если пустых блоков в БД нет, то БД расширяется физически, увеличивая последний экстент переменной длины. Каждое увеличение добавляет к БД число блоков, кратное 16. При первом расширении добавляется 16 блоков, затем 32, потом 48, 64 и т.д. до 128. После этого при каждом расширении добавляется по 128 блоков. Вновь добавленные блоки становятся пустыми и размещаются выше high-water mark. Если последний экстент имеет фиксированный размер, то БД не может физически расширяться и операция отменяется, и сессия завершается. Экстенты фиксированной длины заполняются пустыми блоками.
Запись RM блоков
RM блоки используются для хранения записей одной или нескольких таблиц. Размер их заголовка переменный и равен, приблизительно, 20 + (2 * число записей в блоке) байт.
Блоки БД размером менее 8 К могут хранить до 32 записей, при условии, что они полностью умещаются в нем. В блоке размером 8 К может храниться до 64 записей. Если запись больше, чем свободное место в блоке, то она разбивается на 2 или более фрагментов. Каждый фрагмент хранится в отдельном блоке. При сохранении записи в одном блоке PROGRESS резервирует часть места на будущее увеличение записи.
Адресация записи
Каждая запись уникально идентифицируется 31-битным значением, называемым recid. Recid определяет относительную позицию записи в БД. Recidы состоят из двух частей, содержащих 25 (8К) или 26-битный (меньше 8 К) dbkey и 5 (меньше 8К) или 6 (8 К) бит отводится на номер записи внутри блока, называемый slot number.
Таким образом, максимальное количество записей в БД адресуемых recid, составляет 2,147,483,392.
Однажды присвоенный записи recid остается неизменным в течение всей жизни записи. При удалении записи её recid может быть использован для вновь создаваемой записи.
Создание записи
При создании записи используются 3 константы:
1. константа расширения пространства (75 байт для всех систем) – менеджер БД пытается оставить некоторое свободное пространство для каждой записи для возможного расширения записи, не прибегая к ее фрагментации
2. константа свободного пространства – является индикатором частично использованного RM блока, т.е. если в блоке имеется свободное место большее или равное значению константы, то этот блок может быть использован для хранения других записей. Частично заполненные RM блоки образуют RM цепочку. Величина константы зависит от размера блока БД.
3. Третья константа определяет кол-во бит в recid, в которых хранится относительный номер записи в RM блоке.
Чтобы определить место для сохранения записи или ее фрагмента, менеджер БД ищет блоки внутри RM цепочки, если блок в начале RM цепочки содержит достаточное кол-во свободного места для сохранения записи, он будет использован.
Если в начале цепочки блок не может быть использован для сохранения записи, то проверяется, является ли он действительно свободным, если нет, он удаляется из RM цепочки, если да, то перемещается в ее конец. Затем менеджер БД переходит к следующему блоку. Если в 100 блоках RM цепочки не удалось сохранить запись, то дальнейший перебор блоков прекращается и блок выбирается из свободной цепочки.
Обновление записи
При обновлении записи существует три ситуации
1. Новая запись имеет тот же самый размер, что и старая, в этом случае просто новая запись заменяет старую.
2. Новая запись меньше, чем старая, неиспользованное пространство добавляется к свободному пространству блока.
3. Новая запись больше, чем старая и в блоке недостаточно место для ее полного сохранения, запись фрагментируется, и фрагмент записи размещается по тому же самому алгоритму, что и при создании.
Удаление записи
Когда происходит удаление, запись заменяется на специальную метку recid lock или place holder. Это необходимо для сохранения recid записи в случае, если произойдет откат транзакции. А локировка recid предохраняет использование этого recid для назначения другим записям. После комита транзакции локировка не снимается сразу, а снимается только при следующем обращении к этому блоку.
Кол-во свободных слотов и размер свободного пространства в блоке изменяются только при создании и обновлении записи во время просмотра RM цепочки. В процессе тестирования блока на размещение в нем данным убираются recid locks.
Индексные блоки
Индексные блоки хранят упорядоченные пары ключевое значение + recid. Каждый индексный блок хранит только упорядоченные значения одного индекса. Если значения индекса не помещаются в одном блоке, то PROGRESS использует размещение значений в нескольких блоках, используя сжатое b-дерево.
Новое индексное значение вставляется в определенное место в блоке, между меньшим и большим значением. Подходящее место для значения выбирается путем обхода b-дерева. Если места для размещения значения блоке не найдено, то блок разбивается на два. Половина записей из блока переносится во вновь созданный блок.
При обновлении индексного значения, старое значение удаляется, а новое добавляется по вышеописанному алгоритму.
Удаление индексного значения в неуникальных индексах сводится к удалению значения и к сжатию оставшихся в блоке значений.
Когда последнее значение в листовом блоке удалено блок становится пустым, обновляется родительская ссылка на этот блок и освобожденный блок возвращается в свободную цепочку.
Удаление значения в уникальном индексном блоке иное, при удалении значения вместо удаленного значения пишется index entry lock, который предохраняет использования этого значения другими транзакциями. В случае отката транзакции ключ восстанавливается.
Локированные индексные блоки помещаются в список, которым владеет активная транзакция, если он не помещен в список другой траназакции. В конце транзакции происходит сканирование списка и удаление индексных локировок.
Определение пространства, используемого БД
"proutil dbname -C dbanalys"
"proutil dbname -C ixanalys"
Запросы
В этом документе описывается функциональность и характер работы запросов в PROGRESS 4gl. Понимание сущности работы запросов поможет вам в повышении эффективности их использования при создании приложений.
4gl queries
4gl queries – это операции управления данными, используемые для поиска или изменения данных, хранимых в БД.
В языке 4gl существуют следующие конструкции, используемые с тремя типами запросов:
FOR EACH / PRESELECT
FOR EACH
REPEAT PRESELECT
DO PRESELECT
FIND
FIND [ FIRST | NEXT | LAST | PREV ]
FIND
FIND CURRENT
FIND record WHERE ROWID (record) = expression.
CAN-FIND ( [ FIRST | LAST ] )
CAN-FIND (record WHERE ...)
GET
DEFINE QUERY
OPEN QUERY
GET [FIRST | NEXT | LAST | PREV]
GET CURRENT
К остальным конструкциям 4gl, связанным с запросами относятся:
- BY – определяет очередность результатов посредством индексов или сортировки.
- USE-INDEX – указывает на необходимость использования конкретного индекса. Обеспечивает очередность в случае неиспользования конструкции с BY.
- SCROLLING – присоединяет список результатов к запросу.
- CACHE – присоединяет кэш запроса и список результатов к запросу.
- REPOSITION – изменяет текущую позицию запроса.
- INDEXED-REPOSITION – позволяет изменить позицию запроса, использующего один индекс, путем прямого переноса курсора индекса.
- NO-WAIT – позволяет избежать ожидания в случае локировки записи и обеспечить непрерывность работы.
- CONTAINS – запрос через индекс слова (word-index).
- FIELDS – определяет, какое поле необходимо извлечь.
Курсоры индексов
Курсор индекса – структура, используемая для сохранения позиции внутри индекса. Он может быть перемещен к следующей, предыдущей или любой требуемой записи ввода индекса и может использоваться для чтения записи в текущем реестре.
Курсоры индекса управляются сервером по запросу клиента. Одновременно можно открывать несколько индексов. Любое взаимодействие между ними (например, изменение их взаимоположения) инициируется только клиентом.
Брэкеты индекса (index brackets)
Index bracket – это совокупность последовательных записей ввода индексов. Брэкет определяется идентификатором индекса (номер индекса), где его наименьшая величина - нижний предел, а наибольшая - верхний предел. В брэкеты заключаются все записи ввода индексов, начиная с наименьшей величины. Просмотр всех брэкетов от нижнего до верхнего пределов называется bracket scan. Просмотр брэкетов (bracket scan) является основным методом извлечения данных и использует курсор индекса.
Существует два класса брэкетов: : equality brackets и range brackets. Equality brackets (брэкеты равенства) определяют совокупность последовательных записей ввода индексов, имеющих одинаковые значения или часть значений которых совпадает. Верхний и нижний предел значений одинаковые.
Range brackets (диапазонные брэкеты) – определяет совокупность записей ввода индексов от нижнего до верхнего пределов их значения. Записи ввода в диапазонных брэкетах часто имеют много различных значений идентификатора индекса.
Для оценки запроса PROGRESS всегда использует, по крайней мере, один брэкет индекса (за исключением случаев поиска с помощью rowid, когда запись извлекается без использования индекса). Компилятор 4gl анализирует запрос на предмет возможного использования элементов WHERE clause, OF, USING и т.д., доступных индексов и компонентов значений для создания брэкетов.
Прежде всего следует использовать самые ограничительные брэкеты. Если запрос не определяет никаких брэкетов, компилятор подставит брэкет по умолчанию, обычно это первый индекс таблицы. Выражение (name BEGINS "M")описывает совокупность записей ввода, где имя начинается с буквы "M". Для этого используется equality bracket. Выражение
((name > "Off The Wall") and (name < "Quick Toss Lacrosse"))
описывает совокупность записей ввода по индексу имени. Здесь используется range bracket.
Использование одного брэкета индекса для извлечения записи
Для извлечения записи с использованием брэкета индекса клиент открывает новый курсор индекса или использует уже открытый. Он затем посылает серверу запрос. Включающий в себя идентификатор курсора и bracket range (верхнее и нижнее значение), и запрашивает следующую, предыдущую, первую или последнюю запись в брэкете. В каждом из нижеприведенных запросов используется брэкет по умолчанию для всего индекса целиком:
for each customer: /* entire table - no where clause */
end.
for each customer by state: /* sort, no index on state */
end.
for each customer
where (state = "OH"): /* no index on state */
end.
В следующих запросах использован один единственный брэкет для части одного индекса:
Использование более одного брэкета индекса для извлечения записи
Запросы с несколькими брэкетами используются операторами GET, FOR EACH и PRESELECT в версиях 7 и более поздних. Они обеспечивают ту же фунциональность, что и курсоры индекса, но могут использовать несколько брэкетов индекса и несколько курсоров индекса, что повышает эффективность работы. Они комбинируют брэкеты индексов с помощью алгоритма, использующего преимущества структуры индекса и улучшают выполнение запросов, содержащих операторы OR and AND. Например, если для исполнения запроса использовался бы один единственный брэкет,
for each customer
where (cust-num <= 10) or (name = "Mary"):
end.
Пришлось бы просматривать всю таблицу, поскольку никакой наименьший брэкет не включает все желаемые записи. При использовании 2 брэкетов возникает необходимость доступа к множеству более мелких записей.
Следующие запросы используют 2 брэкета для индекса почтового кода:
for each customer
where (zip = 12345) or (zip > 40000):
end.
for each customer
where ((zip > 50000) and (zip < 60000)) or
((zip > 70000) and (zip < 80000)):
end.
Запросы по извлечению записей с использованием нескольких брэкетов обычно более эффективны (обрабатываются быстрее), чем с использованием одного курсора индекса, но их позиция в отличие от курсоров индекса не может быть изменена. Запросы с использованием нескольких брэкетов могут быть только перенесены вперед к следующей, и операции GET PREV обеспечиваются клиентом посредством списка результатов.Запросы с использование одного брэкета могут быть перемещены путем изменения позиции нижележащего курсора индекса. Это можно осуществить с помощью опции INDEXED-REPOSITION.
Выбор записей
Выбор записей означает определение соответствия записи запросу путем оценки выражения, включающего ее содержимое, а не нахождения ее с помощью индекса. Проверка осуществляется путем оценки выражения в форме "E-code" – возвращающего результат истинно или ложно в отношении записи. Например, в запросе:
for each customer
where (cust-num < 10) and (city = "Boston"):
end.
где поле cust-num индексируется, а поле city - нет, PROGRESS совершает следующие действия:
- Извлекает все записи, где cust-num < 10 при помощи брэкета индекса для индекса cust-num
- Осуществляет выбор каждой извлеченной записи для выявления, содержит ли она значение "Boston" в поле city (город).
Важно отметить, что существуют некоторые операции по отбору записей, которые сервер не может осуществить в силу того, что они либо требуют доступа к программным переменным клиента, или потому что они не реализованы на сервере. К таким операциям относится CAN-FIND, который не реализован на сервере. В этих случаях сервер посылает записи клиенту с указанием, что он не может выполнить запрос и клиент должен сделать это сам.
Запросы по слову (word indexes)
Запрос по слову выполняется с использованием оператора CONTAINS, который использует word-index. По своей структуре word-index полностью совпадает со структурой обычных индексов, но в индексных значениях содержатся все слова символьного поля.
Оценка выражения содержащего CONTAINS отличается от оценки других индексных выражений:
- Кол-во индексных брэкетов определяется во время выполнения запроса сервером.
- Использование CONTAINS всегда требует использование word-index
Поиск по ROWID
Поиск по ROWID не требует использования никакого индекса.
Поиск уникальной записи через FIND
FIND без модификаторов NEXT PREV и т.д. выполняет операцию, которая отличается от других FIND запросов, а именно он не только ищет запись, но и гарантирует, что найденная им запись является единственной в запросе. Если более чем одна запись находится, то возвращается ошибка. Для обеспечения такой функциональности, PROGRESS необходимо найти первую запись, а затем посмотреть нет ли еще записей. 2-й шаг является очень неэффективным. Особенно если запрос делается не сервером, а клиентом.
Объединение
Объединение – это операция для объединения двух или более таблиц в одну путем сопоставления величины полей. PROGRESS транслирует объединения в несколько однотабличных запросов, которые на сегодняшний день только и могут обрабатываться сервером.
Списки полей
Выражения FIELDS и EXCEPT требуют от PROGRESS извлечения только определенных полей из записи. Это позволяет снизить количество байт, посылаемых по сети. Но не снижает количество операций ввода/вывода, выполняемых сервером. Просто сервер не включает ненужные поля перед отправкой их клиенту. В однопользовательском режиме или режиме разделяемой памяти сокращение размера записи не приводит к росту производительности. Тем не менее PROGRESS отбрасывает ненужные поля для выявления ошибки в программе, которая произойдет при запуске этой программы в режиме клиент-сервер.
PROGRESS автоматически расширит список полей в случае их неявного использования, например в объединении.
Предварительная выборка записей в сетевых сообщениях
Для снижения сетевого трафика сервер может упаковывать несколько записей в один сетевой пакет при выполнении запроса. Эта операция называется prefetch, т.к. сервером пересылаются дополнительные записи, которых клиент еще не требовал.
Инициация этой операции исходит от клиента, когда клиент посылает запрос на сервер, он устанавливает два флага, первый указывает, что он бы хотел получить больше чем одну запись в пакете, когда это возможно. Второй указывает, что он хочет получить от сервера полностью заполненный пакет. В случае установки первого флага, сервер попытается выбрать оптимальное решение, чтобы понизить кол-во сетевых пактов и обеспечить хорошее время отклика. Т.е. в жтом случае сетевой пакет может быть не полностью заполнен. Если установлены два флага, то сервер не будет отвечать пока полностью не заполнит сетевой пакет.
Клиент устанавливает оба флага, когда выполняет запрос с опцией PRESELECT или когда выполняет сортировку результата.
Первый флаг устанавливается, когда выполняется FOR EACH или scrolling запрос с опцией NO-LOCK, если он не содержит NO-PREFETCH опции.
Ограничение NO-LOCK’ом связано с тем, что при выборке будут локироваться записи, которые еще не затребованы клиентом.
Список результатов
Список результатов или result list представляют собой список ROWID записей, которые попали в запрос. Они хранятся на клиенте и обновляются когда поступают результаты от сервера. Они используются для выполнения операций таких как GET PREV, PRESELECT запросами и запросами, в которых результат сортируется.
Сортировка результатов запроса
Сортировка выполняется при указании запросу опции BY. Сортировка выполняется двумя способами:
1. Если имеется индекс, по которому производится сортировка и используется только один индексный брэкет, который может быть использован для запроса, то сортировка выполняется в процессе запроса.
2. В противном случае, происходит двухэтапный процесс. Каждая запись читается и поля которые участвуют в сортировке обрабатываются. Значения этих полей и их ROWID помещаются в список результатов, который затем сортируется. И во втором проходе записи считываются снова, используя отсортированный ROWID.
Позиционирование в запросе
По запросу со списком результатов можно позиционироваться, используя оператор REPOSITION. Большинство таких операций выполняется клиентом, используя список результатов, и не требуют вмешательства сервера. Оператор REPOSITION может обрабатываться сервером только, если указана опция INDEXED- REPOSITION и используется один индексный брэкет по однотабличному запросу.
Кэш запроса
Кэш запроса – это механизм на стороне клиента, содержащий последние записи, к которым было обращение запроса в клиентском буферном поле. Это повышает скорость просмотра запроса операторами GET PREVIOUS и GET NEXT в цикле.
Кэш запроса задается опцией CACHE в операторе DEFINE QUERY. Оно создается по умолчанию для запросов, связанных с браузерами.
Кэш запроса может быть использован только для запросов с опцией NO-LOCK.
Стратегия выполнения
Почти все запросы используют различные способы получения желаемых данных. Эти способы включают:
- Какой индексный брэкет использовать
- Как использовать индексные брэкеты в сочетании друг с другом
- Какие выражения использовать для выбора записи
Стратегия выполнения запроса определяется компилятором. Структура данных, описывающих стратегию выполнения, хранится в r-code. Там, где есть возможность, компилятор использует множественные брэкеты. Для выражений, содержащих подвыражения, соединенные оператором ИЛИ, будут использованы подходящие индексные брэкеты для обоих выражений.
Для выражений с WHERE , которые содержат подвыражения с оператором AND множественные брэкеты будут использоваться, когда все компоненты сопоставляются на равенство и включены в простой индекс, не являющийся уникальным.
Компилятор использует следующую систему приоритетов при выборе доступных индексов:
1. Индекс, который указывается в опции USE-INDEX
2. Уникальный индекс, когда все ключевые компоненты сопоставляются на равенство
3. Индекс с наибольшим количеством сопоставлений на равенство
4. Индекс с наибольшим количеством сопоставлений по интервалу
5. Word-index, на который ссылается оператор CONTAINS
6. Индекс с наибольшим количеством сопоставлений по сортировке
7. Первичный индекс
8. Первый по алфавиту индекс
Характеристики запросов FOR EACH
SORT
|
JOIN
|
DIRECTION
|
FIELD LISTS
|
PREFETCH |
CACHE
|
VARIABLE BINDING
|
QBW
|
yes |
yes |
forward only |
yes |
with no-lock; at presort |
no |
once, before entering block |
yes |
FOR EACH запросы выполняются внутри итерационного блока 4 gl. Следующая запись всегда извлекается сверху блока.
При использовании множественных индексов порядок извлеченных записей «случаен». Иначе он базируется на выбранном индексе. Если возможно использование множественных индексов фразы BY и USE-INDEX могут использоваться для обеспечения определенной очередности.
Выбор записей в FOR EACH запросе всегда происходит последовательно. Следующая запись выбирается в начале каждой итерации. Случайный доступ к записи исключен.
Оценка переменных в выражении WHERE выполняется только один раз перед входом в блок. FOR EACH запросы не кэшируются, т.к. кэш полезен при случайном доступе к записям.
С опцией NO-LOCK prefetch выполняется по умолчанию, но может выключаться опцией NO-PREFETCH.
Характеристики PRESELECT запросов
SORT
|
JOIN
|
DIRECTION
|
FIELD LISTS
|
PREFETCH
|
CACHE |
VARIABLE BINDING
|
QBW
|
yes |
yes |
first, next, prev, last |
yes |
only at pass 1
|
no |
once, before entering block |
yes |
PRESELECT запросы выполняются сервером также как и FOR EACH. На клиенте PRESELECT запросы обрабатываются двумя проходами, также как в случае с сортировкой запросов FOR EACH. Сначала все удовлетворяющие запросу записи считываются и формируют список результатов со всеми ROWID. Список сортируется в случае необходимости. Затем, когда происходит обращение к записи через FIND, она считывается снова по ROWID и передается программе.
PRESELECT запрос указывается в заголовке блоке, такого как REPEAT или DO. Запрос отрабатывается до входа в блок, записи считываются оператором FIND.
Порядок считывания записей не обеспечивается, кроме случаев явного указания опций BY и USE-INDEX.
Доступ внутри запроса случаен. Переменные в выражении WHERE определяются до входа в блок, как и для FOR EACH. Запрос не использует кэш.
PREFETCH используется при первом проходе запроса и не может быть отключен.
Характеристика FIND запросов
SORT
|
JOIN
|
DIRECTION
|
FIELD LISTS |
PREFETCH |
CACHE |
VARIABLE BINDING
|
QBW |
no |
no |
first, next, prev, last, auto |
no |
no |
no |
for every record |
no |
FIND запрос всегда использует один индексный курсов и один индексный брэкет.
Выражение WHERE или опция USE-INDEX определяют, какой из индексов будет использован. Индекс и буфер, используемы в запросе определяет, какой индексный курсор будет использован. Например, если два запроса используют тот же буфер и индекс, они используют один индексный курсор. Индексный курсор не принадлежит конкретному FIND и может использоваться несколькими FIND. Таким образом, один FIND может изменить позицию индексного курсора, которая используется другим запросом.
Индексный курсор, используемый FIND распространяется на всю область видимости связанного буфера. Порядок возвращения записи определяется индексом, который используется. Выражение BY не допустимо.
Помимо операторов FIND NEXT, PREV, FIRST и LAST, запросы FIND могут быть также позиционированы автоматически другим запросами: когда FIND или FOR EACH считывают запись, все индексные курсоры, которые используются запросами FIND для одно и того же буфера, позиционируются на ту же самую запись.
Определение значений переменных в WHERE выражениях происходит каждый раз перед выполнением очередного запроса. FIND запросы не кэшируются и не используют PREFETCH.
Характеристики GET запросов
SORT
|
JOIN
|
DIRECTION
|
FIELD LISTS |
PREFETCH |
CACHE |
VARIABLE BINDING
|
QBW |
yes |
yes |
first, next, prev, last, reposition |
yes |
with scrolling no-lock; at presort |
yes |
once, before opening query |
yes |
GET запросы используют мультибрэкеты, когда это возможно. GET запросы не относятся к конкретному блоку и распространяются на все блоки и процедуры в области видимости запроса. Порядок извлечения записей случаен, но может ограничиваться опциям BY и USE-INDEX.
Для позиционирования внутри запросов используются операторы GET PREV, LAST и т.д., а также оператор REPOSITION.
Оператор GET NEXT выполняется для любого запроса, тогда как остальные GET выполняются только для следующих запросов:
- С указанной опцией SCROLLING
- С использованием одного индексного курсора.
Оператор REPOSITION поддерживается только для скролируемых запросов, т.к. требует список результатов для своего выполнения. Большинство операций выполняются клиентом, используя список результатов. Сервером могут выполняться следующие операции:
- Все запросы получения следующей записи
- Запросы, использующие один индексный брэкет, могут возвращать первую, предыдущую и последнюю запись, просто позиционируясь по индексному брэкету. Это схоже с запросами FIND.
- REPOSITION TO ROWID может быть отработан на сервере, если ROWID не содержится в списке результата и включена опция INDEXED-REPOSITION.
Операции со списком результатов требуют больше времени на выполнение, но не смотря на это, список результатов обеспечивает удобную навигацию внутри запроса.
Определение значений переменных происходит единожды перед выполнением запроса. GET запросы могут кэшироваться. По умолчанию, кэш используется, если запрос связан с браузером.
PREFETCH также используется, если указаны опции SCROLLING или CACHE и опция NO-LOCK. Для выключения PREFETCH используется опция NO-PREFETCH. Если используется сортировка, то в пакеты включается максимальное число записей.
Когда какой запрос лучше использовать
FOR EACH запросы полезны для последовательного перебора записей, когда необходимо обработать один раз каждую запись. Например, отчеты или начисление комиссии.
PRESELECT имеет два свойства, которых нет у FOR EACH.
Произвольный доступ к записям
Фиксированный набор записей, возвращаемый запросом, не смотря на обновления. Это гарантирует, что каждая запись будет возвращена один раз. Если не используется список результата, то обновление записей в таблице может привести к тому, что одна и та же запись будет возвращена несколько раз.
for each customer:
cust-num = cust-num + 2.
end.
FIND запросы имеют следующие преимущества:
FIND NEXT, PREV, FIRST и LAST работают с одинаковой скоростью и не требуют никаких затратных операций, таких как предварительная выборка.
Автоматическое позиционирование индексных курсоров позволяет осуществлять случайный доступ к записи оптимальным образом.
Эти преимущества очень полезны для интерактивных приложений, таких как просмотр.
FIND запросы имеют следующие недостатки:
Используется только один индексный брэкет
Имеется возможность объединить несколько запросов FIND, но это чревато возникновением ошибок.
Эти запросы не использую выражения BY, и сортировка ограничена выбранным индексом.
GET запросы были добавлены в 7 версию, чтобы заменить FIND запросы в большинстве случаев. Они в значительной мере потеснили FIND запросы, используемые для просмотра записи, особенно с использованием браузера. Они также поддерживают SQL.
GET запросы имеют следующие преимущества:
- Они эффективны, потенциально могут использовать множественные индексные брэкеты.
- Они не связаны с блоками, распространяются на всю процедуру или модуль.
- Они обеспечивают дополнительную навигацию с помощью оператора REPOSITION.
- Они обеспечивают объединение, сортировку и предварительную выборку.
Недостатки GET запросов:
- Некоторые опции, используемые при перемещении внутри запроса иногда замедляют работу, например GET LAST для мульти-индексного запроса.
- Опция INDEXED-REPOSITION, которая обеспечивает навигацию в стиле FIND запросов, трудна для использования, т.к. она не работает с объединениями и множественными индексами. Кроме того, сложно предсказать и понять поведение списка результатов с этой опцией.
- При использовании GET запросов приходится печатать больше, чем для FIND, по крайней мере нужны 3 оператора: DEFINE QUERY, OPEN и GET.
Written by © Serguey Klimoff, 2003, Russia
|
|
|
|