Это две основные причины, которые препятствуют всеобщей индексации. Помимо них есть и еще несколько замечаний, ограничивающих применение индекса. Первое - это правило 20 %. Оно гласит, что если запрос на выборку возвращает более 20 % записей из таблицы, то использование индекса может замедлить выборку данных! Конечно, ситуация зависит от конкретного запроса и условий, наложенных на выборку, но нужно помнить, что 20 % записей являются порогом, когда эффективность использования индексов ставится под вопрос. Второе замечание формулируется не так очевидно. Оно связано с работой оптимизатора InterBase.
Оптимизатор - это совокупность механизмов, которые разрабатывают таи выполнения запроса. Когда пользователь передает InterBase какой-либо SQL-запрос, он указывает, ЧТО должен вернуть сервер в результате выполнения запроса, но не определяет, КАК сервер должен выполнять запрос. Оптимизатор на основе переданного запроса строит план его выполнения, т. е. откуда и в каком порядке будут браться данные для выполнения запроса, какие индексы будут при этом использоваться. Когда сервер анализирует условия на выборку (это в основном части выражения WHERE, ORDER BY и т. д.), то для каждого поля, входящего в условие, сервер пытается использовать индекс. К сожалению, алгоритм создания плана несовершенен и оптимизатор часто использует индексы, которые не слишком эффективны для конкретного запроса, из-за чего может существенно замедлиться время выполнения. Поэтому создание лишних индексов может привести к созданию неоптимальных планов.
Надо отметить, что в клоне Yaffil эта проблема разрешена за счет использования современных алгоритмов построения пчанов.
Третьим случаем, когда индекс не нужен, являются поля с ограниченным набором значений - например, поле, которое хранит информацию о поле человека и содержит только два возможных значения - "м" и "ж"; нет никакого смысла индексировать это поле.
Итак, основные ограничения на создание индексов мы рассмотрели. Теперь следует рассмотреть вопрос, когда следует использовать индексы, чтобы добиться улучшения производительности. Существует 3 основных случая, когда необходимо проиндексировать поле:
* Когда это поле используется в условиях поиска в запросах.
* Когда соединения таблиц (JOIN) используют это поле.
* Когда это поле используется в предложениях сортировки ORDER BY.
Если поле применяется указанным выше образом, то создание индекса на него может привести к улучшению производительности запроса.
Давайте рассмотрим синтаксис создания индексов. Вот полный формат команды DDL, который позволяет создавать индексы:
CREATE [UNIQUE] [ASC[ENDING] | DESC[ENDING]]
INDEX index ON table (col [, col ...]);
Минимальным выражением, создающим индекс, является следующее:
CREATE INDEX my_index ON Table_example(ID)
В этом примере создается индекс с именем my_index для таблицы Table_example, причем индексированным полем является поле ID. Индекс является возрастающим, т. е. значения в нем упорядочены по возрастанию, а также неуникальным, т. е. значит, что поле ID может иметь несколько одинаковых значений. Это, конечно же, самый простой пример индекса - самый распространенный.
Как видно из описания синтаксиса, индекс может содержать не одно, а несколько полей. Такой индекс используется при часто выполняющихся запросах, которые содержат в условиях поиска или сортировки сочетание индексированных полей. Например, если у нас есть таблица, содержащая поля Фамилия, Имя, Отчество, то при запросе, использующем сортировку по ФИО, будет применен 1акой индекс. Вообще говоря, необязательно вводить условия на все 3 поля, применяемые в индексе, чтобы использовать его преимущества. Если мы желаем сортировать результат запроса, то индекс будет использован в случае, если первое поле в условии сортировки совпадает с первым полем в индексе, например наш индекс будет задействован в случае сортировки по Фамилии и Имени.
В документации для оптимизации выполнения запроса, содержащего в предложении WHERE соединение полей с условием OR рекомендуется, использовать не составной индекс, а несколько одинарных индексов на все поля, входящие в условие OR.
К вопросу о порядке сортировки индекса: как видно, он может быть либо возрастающим (ASQENDING]), либо убывающим (DESCENDING]). Зачем нужны разные порядки сортировки? Очевидно, для разных сортировок! Если мы желаем сортировать людей по фамилии в возрастающем порядке, то создаем возрастающий индекс (ASC), а если в убывающем (от Я до А) - то убывающий! А если хотим и то и другое, то необходимо создавать оба индекса.
Обеспечение ссылочной целостности с помощью индексов
В определении индекса имеется еще одна опция - UNIQUE. Если ее указать, то индекс позволит заносить в таблицу только уникальные значения. Фактически это служит основой для реализации уникальных ключей (UNIQUffi KEY). Уникальные ключи широко используются в базах данных. То есть РК - это уникальный ключ-индекс, но не всякий UK - это РК. Выше речь шла только о РК. Первичный ключ (Primary key) - самый распространенный вид уникального ключа. При создании первичного ключа на таблицу автоматически создается уникальный индекс, который получает имя, составленное из RDBSPRIMARYNNN, где NNN - последовательный уникальный в пределах базы данных номер. Таким образом, с помощью уникального индекса реализуются два из важнейших ограничений ссылочной целостности - уникальный ключ и первичный ключ. Очевидно, что понятие уникальности несовместимо с понятием неопределенного значения, т. е. другими словами, в полях, содержащихся в уникальных индексах, не должно быть значений типа NULL. Перед созданием уникального индекса на поле следует придать ему статус NOT NULL. Если индекс создается для уже существующих данных, то при создании будет проверено, не содержит ли индексированное поле повторяющихся значений. И если содержит, то в создании индекса будет отказано.
Помимо ограничений уникального и первичного ключа, механизм индексов лежит в основе реализации еще одного ограничения ссылочной целостности - внешнего ключа. Ограничение внешнего ключа накладывается на одно или несколько полей какой-либо таблицы и препятствует внесению в эти поля таких значений, которые не входят в первичный ключ другой, родительской таблицы. Для реализации внешнего ключа, т. е. для осуществления проверки того, существует ли значение в родительской таблице, автоматически создается особый индекс. Он имеет наименование RDB$FOREIGNNN, где NNN - последовательный у никальный в пределах базы данных номер.
Почему именно механизм индексов используется для реализации ограничений ссылочной целостности? Дело в том, что индексы в InterBase находятся в особом, привилегированном положении - говорят, что они выполняются вне контекста транзакций. Это очень важное свойство. О транзакциях мы поговорим позже, в посвященной им главе, а пока лишь скажем, что нахождение индексов вне транзакций означает, что все пользователи, одновременно работающие с данными в одной и той же таблице, вынуждены соблюдать ограничения ссылочной целостности.
Оптимизация производительности индексов
В названии этого раздела можно обнаружить некоторый парадокс - индексы, как говорилось выше, служат для того, чтобы ускорить выполнение запросов, и оказывается, что их самих надо тоже оптимизировать! Но что делать (такова жизнь) - кто-то должен заботиться и об индексах.
Что же случается с индексами? Почему они "теряют форму"? Нам придется еще раз сказать о том, что индексы реализованы в виде двоичного дерева И когда в таблицу добавляется (изменяется, удаляется - выберите по вкусу) новая запись, в дерево добавляется новая веточка. Причем веточки добавляются не в середину дерева, а на концах других веточек. Постепенно дерево становится все более "раскидистым" (также говорят - несбалансированным), а поиск по нему - все менее эффективным. Поправить положение может перестройка дерева или (в некоторых случаях) пересчет статистики. Периодически требуется пересоздавать индекс, чтобы восстанавливать его производительность. Пересоздание индекса происходит в следующих случаях: