|
Индексы в ABL
Узнать, какие индексы AVM (Abl Virtual Machine) будет использовать во время выполнения конкретного запроса можно с помощью опции XREF в операторе COMPILE. Строки SEARCH в выходном файле XREF указывают на индексы, к которым происходит обращение каждым оператором выборки записи. Следующая таблица содержит список тэгов, которые генерирует опция XREF при компиляции.
Тэг |
Значение |
SEARCH |
Указывает
индексный брэкет, который будет использоваться. Перечисляются
логическое имя базы данных, имя таблицы, и имена индексов. Когда
будут использоваться множество брэкетов и индексов для одного и
того же запроса, здесь Вы будете видеть одну строку поиска для
каждого брэкета. |
SEARCH ...
WHOLE-INDEX |
Указывает, что подходящий брэкет не мог быть создан, и будет
выполнено индексное сканирование по всей таблице, используя
указанный индекс. |
SORT-ACCESS |
Указывает, что результат запроса группируется по конкретному
столбцу, и никакого подходящего индекса не существует.
|
ACCESS |
Указывает, что таблица и значение поля используются в этой точке
программы. |
CREATE |
Указывает, что в этой точке программы создана запись. |
DELETE |
Указывает, что в этой точке программы удалена запись. |
UPDATE |
Указывает, что в этом месте указанное поле таблицы обновлено. |
Альтернативным методом, для определения использования индексов может быть анализ индексной статистики в виртуальных системных таблицах. Параметры запуска, которые активируют эти таблицы, описаны в OpenEdge Data Management: Database Administration.
AVM создает новый элемент указателя для записи при возникновении следующих условий:
- В конце оператора, в котором AVM устанавливает значения всем компонентам индекса.
- В конце самого близкого повторяющегося блока субтранзакции, в котором AVM создает запись.
- Когда AVM обрабатывает оператора VALIDATE.
- Когда AVM освобождает запись в буфере записей.
- В конце транзакции, в которой AVM создает запись.
AVM обновляет индекс в конце любого оператора, в котором он изменяет значения для одного или более индексных полей. Поскольку индексы обновляются AVM немедленно (в конце оператора UPDATE), AVM немедленно может найти записи в порядке нового индекса, в то время как данные в найденной записи неизменны. AVM изменяет данные в записи в конце области видимости записи или когда запись освобождается.
Примечание: AVM не обновляет индекс, если значение, которое Вы пытаетесь назначить индексному полю, является тем же самым что и текущее значение этого индексного поля.
Вы можете поменять имя индекса в любое время. Вы можете также удалить не первичные индексы. Однако, прежде, чем позволить Вам удалять первичный индекс, OpenEdge требует, чтобы Вы сначала определили другой индекс как первичный.
Если есть только один индекс, Вы должны создать новый индекс прежде, чем удалить существующий индекс. Вы не можете изменить ни один из составляющих компонентов индекса. Вместо этого Вы должны удалить индекс и пересоздать его уже с новыми компонентами.
Помните, что OpenEdge предполагает, что первый индекс, который Вы создаете, является первичным индексом, так что всегда сначала создавайте первичный индекс.
Использование оператора ABL ASSIGN
Когда Вы хотите произвести изменения в нескольких индексированных компонентах в ABL процедуре, используйте оператора ASSIGN, чтобы установить эти новые значения. Этот метод позволяет Вам изменять несколько значений с минимальными затратами на обработку ввода - вывода. Иначе, AVM повторно индексирует записи в конце каждого оператора, который изменяет значение индексного компонента.
Следующий код демонстрирует, как Вы можете изменить два индексных значения одним оператором ASSIGN:
DEFINE VARIABLE neword LIKE order-line.order-num LABEL "New Order".
DEFINE VARIABLE newordli LIKE order-line.line-num LABEL "New Order Line".
REPEAT:
PROMPT-FOR order-line.order-num line-num.
FIND order-line USING order-line.order-num AND line-num.
SET neword newordli.
FIND order WHERE order.order-num = neword.
ASSIGN
order-line.order-num = neword
order-line.line-num = newordli.
END.
Эта процедура изменяет номер заказа и номер элемента заказа. (Она копирует строку заказа от одного заказа в другой.) Здесь устанавливаются новые значения в переменные, и запись изменяется единственным оператором ASSIGN, который содержит две фразы присваивания в формате field = expression. Таким образом, оба поля изменены в пределах единственного оператора. Поскольку order-num и line-num используются совместно в одном индексе, этот метод исключает необходимость выполнять индексацию при изменении обоих значений. Индексы и неизвестные значения
Если индекс содержит неизвестное значение (?), то при сортировке AVM оценивает их выше, чем любое другое значение. Когда Вы определяете уникальный индекс, ABL гарантирует эту уникальность. Например, если Cust-num это уникальный индекс, и уже есть Cust-num со значением 10, то ABL не позволит Вам создать еще один Cust-num с таким же значением 10. Однако ABL не мешает пользователям вводить любое количество записей с неизвестными значениями в индексных полях. Чтобы пользователи не делали этого, Вы можете воспрепятствовать им, определив поля уникального индекса обязательными (mandatory).
Пример 1: Используя базу данных Sports, следующий запрос отобразит все записи, где Cust-num > 10, потому что Cust-num индексированное поле и значение Unknown (?) при сортировке имеет высокий приоритет в нем:
FOR EACH cust WHERE cust-num >10 AND cust-num <= ?
Однако запрос ниже отобразит нулевые записи, потому что Cust-num это выбранный для запроса индекс. Так как индекс ZIP не является выбранным индексом, неизвестное значение (?) не будет сортироваться, и вторая часть запроса будет ложью. Никакие записи не возвращаются, когда одна часть AND это FALSE: FOR EACH cust WHERE cust-num >10 AND cust-num <= ? AND zip >0 AND zip <?
Пример 2: то же самое правило может затронуть запросы, где неизвестное значение (?) явно не используется. Если Вы создадите три записи по заказу в базе данных Sport, где order.cust-num = 1 и order-data = ?, тогда следующий запрос возвратит три записи: FOR EACH order WHERE order-date >= 1/1/97
Однако следующий запрос не возвратит записей: FOR EACH order WHERE order-date >= 1/1/97 AND cust-num = 1
Индексы и чувствительность к регистру
Значения для индексированных полей обычно сохраняются как все прописные буквы. Это гарантирует, что AVM сортирует символьные значения должным образом без учета регистра. Например, обработка символов строки "JOHN", "John", и "john" будет выполняться одинаково. Кроме того, если " JOHN" уже находится в уникальном индексе, то любая попытка вставить "John" будет отклонена.
Для полей, зависящих от регистра, AVM сохраняет значения, точно так, как они были введены. Это означает, что в вышеупомянутом примере, "John" воспринимается как различное значение. Кроме того, при сортировке, значения верхнего регистра будут идти первыми, а за ними будут идти значения в нижнем регистре. Так для того же самого примера, "JOHN", "John", и "john", они будут появляться в различном порядке. Однако слова в индексных полях чувствительных к регистру рассматриваются как не чувствительные к регистру.
Чувствительность к регистру это характеристика поля таблицы, но не индекса. Поэтому, если индекс содержит некоторые поля, которые чувствительны к регистру и некоторые, которые таковыми не являются, тогда применяются различные правила сортировки.
Имена полей не чувствительны к регистру; они могут быть в верхнем регистре, в нижнем регистре, или комбинированными. Если Вы в Data Dictionary называете поле именем "Phone", то в своих процедурах вы можете к нему обращаться как "phone" или "PHONE".
Обычно, символьные поля в OpenEdge не чувствительны к регистру ("SMITH" = “Smith” = "smith"). Однако, в редких случаях, вам может понадобиться определить поле, которое чувствительно к регистру. Например, некоторый код, который содержит и символы в верхнем регистре, и символы в нижнем регистре, такой код должен быть сохранен в поле, зависящем от регистра. Зависящие от регистра поля не рекомендуются, потому что они отклоняются от стандартов использования OpenEdge. Однако если Вам необходимо строго придерживаться стандарта ANSI SQL, возможно, придется определить все символьные поля как чувствительные к регистру. Если поле не компонент индекса, то вы можете устанавливать его чувствительность к регистру как угодно. Если же это поле компонент индекса, Вы должны сначала удалить индекс, и пересоздать его уже с измененным полем.
Зависящие от регистра поля могут быть индексированы, и они могут быть сгруппированы с независящими от регистра полями компонентами индекса. В зависящем от регистра индексе, значения "JOHN", "John", и "john" будут уникальными. Однако порядок их сортировки будет зависеть от кодовой страницы Вашей базы данных. Заметьте, что Вы можете определять зависящие от регистра переменные, чтобы хранить значения, которые могут присваиваться чувствительным к регистру полям. Для получения дополнительной информации о чувствительности к регистру, см. параметр запуска ANSI SQL (-Q) в OpenEdge Deployment: Startup Command and Parameter Reference. Как ABL выбирает индексы, чтобы обслуживать запросы
Осознание важности создания индексов для поддержки основных шаблонов доступа к данным, является большим шагом к эффективному дизайну. Однако чтобы сделать эффективное сотрудничество Вашего кода запросов и индексов, Вы должны понять, как ABL выбирает индексы, чтобы обслуживать конкретный запрос.
Эффективное кодирование запросов выходит за рамки этой книги, но значимость этого момента не может быть занижена. Поэтому, этот раздел кратко объясняет, как ABL выбирает самые эффективные индексы, основываясь на ABL запросе. Затем вы узнаете, как ABL, если это возможно, строит индексные скобки (брэкеты) чтобы минимизировать количество выбираемых записей.
Основы и терминология
Этот раздел объясняет основные понятия и терминологию, которая понадобится для обсуждения, как ABL выбирает самые эффективные индексы, чтобы удовлетворить запросу. В целях эффективного выбора индекса существует три общих типа выражения WHERE.
Синтаксис
WHERE searchExpr [ BY field ]
WHERE Cust-num > 6 WHERE Name "D" BY Sales-Rep WHERE Country = "Zimbabwe" WHERE Comments CONTAINS "Com*"
WHERE searchExpr AND searchExpr [ BY field ]
WHERE Cust-num > 6 AND comments CONTAINS "ASAP" WHERE Name = "Rogers" AND Postal-Code BEGINS "017"
WHERE searchExpr OR searchExpr [ BY field ]
Например: WHERE Cust-num > 1000 OR Sales-Rep BEGINS "S" WHERE Postal-Code <= "01500" OR Postal-Code >= "25000"
Дополнительное выражение BY field налагает порядок сортировки на возвращаемые записи и называется соответствием сортировки (sort match). У searchExpr обычно есть одна из следующих форм:
BY field |
Соответствие
сортировки |
field = expression |
Соответствие равенства |
field < / <= / > / >= expression |
Соответствие диапазона |
field BEGINS expression |
Соответствие диапазона |
wordIndexedfield CONTAINS
stringExpression |
Диапазон (строка символьного шаблона)
|
Для получения дополнительной информации, см. Record Phrase и операторе FOR в OpenEdge Development: ABL Reference.
Поскольку эти выражения эффективно выбирают записи для возврата, и индексы для использования — они называются условиями поиска (search conditions). Обычно, но не всегда, field это индексированное поле. Кроме того, условие поиска может включать другие условия поиска, присоединенные с помощью условных операторов AND и OR, тем самым формируя произвольно сложные запросы.
Компилятор создает логическое дерево из запроса и оценивает обе стороны каждого AND или OR, ища индексные критерии. ABL считает пары равенства, диапазона, и сортировки (для OR), и использует их, чтобы выбрать и брэкетировать индексы. Точные правила являются многочисленными и сложными, и неважно полностью знать их подробности. Далее правила описываются достаточно детально, чтобы помочь Вам выработать навыки использования индексов. Кроме того, Вы должны поэкспериментировать, выполняя кодирование различных запросов, компилируя их с опцией XREF, и изучая использование ими индексов, по строкам SEARCH в XREF файле.
Следующие примеры индексирования основаны на базе данных Sports.
Вариант 1: WHERE searchExpr
Если есть индекс по field в выражении searchExpr, или если field это первый компонент в многокомпонентном индексе, то ABL будет использовать этот индекс. Иначе, ABL воспользуется первичным индексом (primary key):
Пример WHERE |
Используемый
индекс |
WHERE Customer.Name BEGINS "B" |
Name |
WHERE Customer.Postal-Code BEGINS "01" |
Cust-Num (первичный индекс) |
Если searchExpr ссылается на поле, которое входит в word-индекс, то ABL будет использовать word-индекс.
Если есть выражение BY Field, и поле индексировано, ABL использует индекс, чтобы сортировать возвращаемые записи, пока нет никакого индекса на выражении WHERE. Если поле не индексировано, ABL создает временную таблицу сортировки и сортирует записи во время выполнения.
Вариант 2: WHERE searchExpr AND searchExpr
Для составного выражения WHERE ABL выстраивает логическое дерево и оценивает возможность использования индексов с обеих сторон AND. Когда WHERE используется с оператором FOR EACH, если обе стороны AND включают пары равенства во всех компонентах неуникальных индексов, используются оба индекса. Когда WHERE используется с оператором FIND, если обе стороны AND это пары равенства по индексированным полям, то используется только один индекс. Заметьте, что выражение word-индекса с простой строкой это пара равенства; а строка-шаблон это пара диапазона:
Пример WHERE |
Используемый
индекс |
WHERE Customer.Name = "Mary" AND Customer.Sales-Rep = "Higgins" |
Name
Sales-Rep |
WHERE Comments CONTAINS "small" AND Country = "USA" AND Postal-Code = "01730" |
Comments
Country-Post |
Если критерии отбора не поддерживают возможность использования множества индексов, см. “Общие правила для выбора одного индекса”.
Если ABL для выборки и возврата записей, использует множественные индексы, то точный порядок возврата не предсказуем. В случае необходимости, Вы можете использовать опции USE-INDEX или BY, чтобы гарантировать необходимый порядок сортировки. В следующем примере с помощью выражения BY гарантируется, что записи будут отсортированы по полю Cust-num:
Пример WHERE |
Используемый
индекс |
WHERE Customer.Country = "USA" AND Customer.Sales-Rep = "Higgins" BY Cust-Num |
Sales-Rep |
Вариант 3: WHERE searchExpr OR searchExpr
Для составного выражения WHERE ABL выстраивает логическое дерево и оценивает возможность использования индексов с обеих сторон OR. Вообще, если все критерии отбора с обеих сторон OR включают пары равенства, диапазона, или сортировки в последующих основных компонентах двух неуникальных индексов, то ABL использует оба индекса:
<Пример WHERE |
Используемый
индекс |
WHERE Customer.Comments CONTAINS "to*" OR Customer.Name = "Carlin" |
Comments
Name |
WHERE Name > "Beaudette" OR Country > "Zambia" |
Name
Country-Post |
Кроме того, если одна сторона OR включает выражение CONTAINS (то есть, использует word-index), то ABL, чтобы удовлетворить другой стороне OR, сначала использует word-index и только затем второй индекс:
Пример WHERE |
Используемый
индекс |
WHERE Comments CONTAINS "credit" OR Postal-Code > "01000" |
Comments
Cust-Num |
В этом примере правая сторона OR включает пару диапазона, но Postal-Code это второй компонент индекса County-Post, таким образом, соответствие не является активным. ABL использует первичный индекс, чтобы удовлетворить этой части запроса и, как всегда, использует word-index, чтобы удовлетворить выражению CONTAINS, как показано в следующем примере:
Пример WHERE |
Используемый
индекс |
WHERE Comments CONTAINS "credit" OR Postal-Code < "01000" BY Sales-Rep |
Comments
Sales-Rep |
Если критерии отбора не поддерживают множественное использование индексов, см. “Общие правила для того, чтобы выбрать единственный индекс”.
Примечание: Если какое-нибудь выражение с обеих сторон OR не использует индекс или все его компоненты, ABL вынужден сканировать все записи, используя первичный индекс.
Общие правила для выбора одного индекса
Когда критерии отбора не поддерживают использование множества индексов, ABL использует следующие общие правила (в указанно порядке), чтобы выбрать самый эффективный индекс:
- Если есть выражение CONTAINS (которое является законным только для полей индексированных word-индексом), используется word-index:
Пример WHERE |
Используемый
индекс |
WHERE Customer.Comments CONTAINS "big" AND Customer.Country = "Canada" |
Comments |
- Если индекс уникален, и все его компоненты используются в активных парах равенства, используется уникальный индекс. Это неизменно возвращает 0 или 1 запись:
Пример WHERE |
Используемый
индекс |
WHERE Customer.Cust-Num = 10 AND Customer.Sales-Rep = "DR" |
Cust-Num |
- Использование индекса с самыми активными парами равенства. Пары равенства являются активными, если оба из следующих условий соблюдаются:
- Они относятся к последовательным, компонентам основного индекса.
- Он связаны с помощью оператора условия AND (не OR или NOT).
В примере, пары равенства дисквалифицируются компонентами 2 и 3 из индекса с тремя компонентами, а так же, на компонентах 1 и 2, если они окружают OR:
Пример WHERE |
Используемый
индекс |
WHERE Customer.Country = "Costa Rica"
AND Customer.Postal-Code >"3001"
AND Customer.Sales-Rep BEGINS "S" |
Country-Post |
WHERE Customer.Name = "Harrison"
AND Customer.Sales-Rep BEGINS "S" |
Name |
WHERE Customer.Name = "Harrison"
AND (Customer.Country = "Finland"
OR Customer.Country = "Denmark") |
Name
|
- Использование индекса с самыми активными парами диапазона. Поскольку пары диапазона, должны быть активными, они должны быть автономными или быть подключены к другим критериями отбора с помощью AND. Кроме того, они должны относиться к индексному компоненту, имеющему любое из двух свойств:
- Компонент является первым или только один в индексе.
- У всех предыдущих компонентов в индексном ключе есть активные пары равенства. Следующая таблица демонстрирует некоторые такие примеры:
Пример WHERE |
Используемый
индекс |
WHERE Customer.Sales-Rep = "ALH"
AND Customer.Country = "Italy"
AND Customer.Postal-Code BEGINS "2" |
Country-Post |
WHERE Customer.Contact = "DLC"
AND Customer.Sales-Rep BEGINS "S" |
Sales-Rep |
WHERE Customer.Contact = "Ritter"
AND Comments CONTAINS "compute*" |
Comments |
- Использование индекса с большинством пар сортировки (все пары сортировки являются активными):
Пример WHERE |
Используемый
индекс |
WHERE Customer.Country BEGINS "EC"
AND Customer.Sales-Rep BEGINS "S"
BY Country |
Country-Post |
WHERE Customer.Contact = "Wilson"
AND Customer.Credit-Limit > 2000
BY Name |
Name |
WHERE Name = "Wilson"
OR Customer.Credit-Limit = 2000
BY Sales-Rep
|
Sales-Rep |
- Использование индекса, который на первом месте в алфавитном порядке. Таким образом, если есть связь, если множественные индексы имеют то же самое количество активных равенств, диапазонов, и/или пар сортировки, используйте алфавит для решения:
Пример WHERE |
Используемый
индекс |
WHERE Customer.Name = "Samali"
AND Customer.Sales-Rep = "BCW" |
Name |
WHERE Customer.Country BEGINS "EC"
AND Customer.Sales-Rep BEGINS "B"
|
Postal-Code |
- Использование первичного индекса:
Пример WHERE |
Используемый
индекс |
WHERE Customer.Contact = "MK"
AND (Customer.Sales-Rep BEGINS "S"
OR Customer.Sales-Rep BEGINS "B") |
Cust-Num |
WHERE Customer.Postal-Code >= "01000"
AND Customer.City = "Boston" |
Cust-Num |
WHERE "meaningless expression" |
Cust-Num |
Брэкетирование
Выбрав один или более индексов, чтобы удовлетворить запросу, AVM пытается немедленно изолировать наименьшее необходимое индексное подмножество, чтобы вернуть насколько возможно меньше записей. Это называется, брэкетирование (bracketing). Тщательный дизайн запроса может увеличить возможности брэкетирования, таким образом, предотвращая ABL от сканирования всех индексов и анализа всех записей. Правила для того, чтобы использовать брэкетирование, просты:
- Брэкетирование активных пар равенства.
- Брэкетирование активных пар диапазона, но никакое дальнейшее брэкетирование не возможно для того индекса.
Следующая таблица демонстрирует некоторые примеры брэкетирования:
Пример WHERE |
Используемый
индекс |
Брэкет |
WHERE Contact = "DLC"
AND (Sales-Rep BEGINS "S"
OR Sales-Rep BEGINS "B") |
Cust-Num |
None
|
WHERE Postal-Code >= "01000"
AND City = "Boston" |
Cust-Num |
None
|
WHERE Name = "Harrison"
AND Sales-Rep BEGINS "S" |
Name |
Name |
WHERE Contact = "DLC"
AND Sales-Rep BEGINS "S" |
Sales-Rep |
Sales-Rep |
WHERE Country BEGINS "EC"
AND Sales-Rep BEGINS "S"
BY Country |
Country-Post |
Country-Post |
WHERE Comments CONTAINS "big"
AND Country = "USA"
AND Postal-Code = "01730" |
Comments
Country-Post |
Country-Post |
Следующие рекомендации предназначены, чтобы помочь Вам улучшить производительность запроса. Это только рекомендации, и при определенных обстоятельствах Вы можете игнорировать некоторые из них:
- Избегайте соединений с парами диапазона с помощью AND.
- Избегите OR, если какое-нибудь выражение с обеих сторон OR не использует индекс (или все его компоненты). Знайте, что AVM будет вынужден сканировать все записи, используя первичный индекс.
- С word-индексами, избегите использования AND с двумя шаблонами в том же самом word-индексе (WHERE comments CONTAINS “fast* & grow*”), или в отдельных word - индексах (WHERE comments CONTAINS “fast*” AND report CONTAINS “ris*”).
- Избегите выражений WHERE, которые с помощью OR связывают word-индекс и неиндексированный критерий (WHERE comments CONTAINS “computer” OR address2 = “Bedford”)
Некоторые рекомендации по индексированию:
- Поскольку количество удовлетворяющих запросу записей, относительно общего количества всех записей, всегда меньше, то это диктует необходимость индексированного доступа.
- Используйте меньше индексов для часто обновляемых таблиц, и больше индексов для часто читаемых, но менее часто обновляемых таблиц.
- Индексная обработка, включая, например, вычисление word-индекса, происходит на стороне OpenEdge сервера, а клиенту возвращается только необходимый минимум записей.
|