Firebird РУКОВОДСТВО РАЗРАБОТЧИКА БАЗ ДАННЫХ
Шрифт:
Abraham Allen
В другой команде переключателя вы можете для isql установить отображение некоторых статистических данных запроса в конце выводимых данных, которые могут оказаться очень полезными при тестировании альтернативных структур запроса и его планов:
SQL>SET STATS ON;
SQL> запускаете ваш запрос>
PLAN..
<вывод>
Current memory = 728316
Delta memory = 61928
Max memory = 773416
Elapsed time =0.17 sec
Buffers = 2048
Reads = 15
Writes = 0
Fetches = 539
Альтернативная команда переключателя SET PLANONLY [ON | OFF]
SQL>SET PLAN OFF;
SQL>SET PLANONLY ON;
SQL>SELECT FIRST_NAMES, SURNAME FROM PERSON
CON>ORDER BY SURNAME;
PLAN (PERSON ORDER XA_SURNAME)
Если вы собираетесь использовать план оптимизатора в качестве стартовой точки для конструирования пользовательского предложения PLAN, то имейте в виду, что синтаксис выводимого выражения идентичен требуемому синтаксису для выражения плана в операторе [80] . Неудобным является то, что в isql не существует возможности выводить план оптимизатора в текстовый файл.
! ! !
СОВЕТ. Графические инструменты, которые отображают планы, обычно предоставляют средства копирования/вставки.
80
Многие аспекты плана, сконструированного внутренне оптимизатором, не видны в плане, показанном в isql и недоступны через синтаксис предложения PLAN для пользовательских планов. Синтаксис показывает только подмножество реального плана, которому будет следовать сервер при выполнении запроса.
. ! .
Следующие примеры используют запросы, которые вы сами можете проверить, используя тестовую базу данных employee.fdb, инсталлированную в ваш каталог примеров [81] .
Этот запрос просто отыскивает все строки из таблицы соответствия в произвольном порядке. Хотя оптимизатор определяет наличие индекса (индекс первичного ключа), он его не использует для поиска:
SQL>SET PLANONLY ON;
81
Поскольку база данных примера не имеет таблиц без индексов, некоторые примеры в этом разделе используют специально описанные неиндексированные версии таблиц EMPLOYEE, PROJECT и DEPARTMENT, названные EMPLOYEE1, PROJECT1 и Departments соответственно. Скрипт с именем NO_INDEXES.SQL для создания этих таблиц может быт загружен с http://www.apress.com.
SQL> SELECT * FROM COUNTRY;
PLAN (COUNTRY NATURAL)
Это все еще простой запрос без соединений:
SQL>SELECT * FROM COUNTRY ORDER BY COUNTRY;
PLAN (COUNTRY ORDER RDB$PRIMARYl)
Оптимизатор выбирает индекс первичного ключа, потому что он имеет запрашиваемое упорядочение; при этом не требуется создание промежуточного потока для сортировки.
Теперь давайте посмотрим, что произойдет, когда мы решим упорядочить тот же самый запрос по неиндексированному столбцу:
SELECT * FROM COUNTRY ORDER BY CURRENCY;
PLAN SORT ((COUNTRY NATURAL))
Оптимизатор выбирает выполнение сортировки, просматривая таблицу COUNTRY В естественном порядке.
Перекрестное соединение, которое обычно создает бесполезный набор, вовсе не использует критериев соединения:
SQL> SELECT Е.*, P.* FROM EMPLOYEE Е, PROJECT Р;
PLAN JOIN (Р NATURAL,Е NATURAL)
Оно
просто сливает каждый поток в левой части с каждым потоком правой части. Оптимизатор не использует индексы. Однако этот пример полезен в качестве введения в соединение между алиасами двух таблиц в спецификации соединения и их использовании в плане.Алиасы, указанные в запросе, используются в плане, выводимом оптимизатором. Для совместимости при создании пользовательского плана хорошей идеей является использование того же способа идентификации таблиц, как и в запросе. При этом, несмотря на то, что синтаксический анализатор не допускает смешивания идентификаторов таблиц и их алиасов в запросах, предложение PLAN допускает любое смешивание [82] . Например, следующий вариант является приемлемым. Обратите внимание, что оптимизатор допускает использование и идентификаторов таблиц в предложении PLAN.
82
Другой причиной использования алиасов при задании запросов и планов является то, что, скорее всего, подобная согласованность будет поддерживаться в следующих версиях Firebird.
SQL> SELECT Е.*, P.* FROM EMPLOYEE E, PROJECT P
CON> PLAN JOIN (PROJECT NATURAL, EMPLOYEE NATURAL);
PLAN JOIN (P NATURAL, E NATURAL)
Такое соединение денормализует отношение один-ко-многим - каждый служащий имеет одну или более записей истории заработной платы:
SELECT Е.*, S.OLD_SALARY, S.NEW_SALARY
FROM EMPLOYEE E
JOIN SALARY_HISTORY S
ON S.EMP_NO = E.EMP_NO;
PLAN JOIN (S NATURAL, E INDEX (RDB$PRIMARY7))
Оптимизатор выбирает цикл по (потенциально) самому длинному детальному потоку для поиска релевантных строк с использованием индекса уникального первичного ключа таблицы EMPLOYEE. В этом примере либо количество строк в каждой таблице будет приблизительно равным, либо количество строк в таблице SALARY_HISTORY не превысит количество строк в таблице EMPLOYEE в той мере, чтобы не достичь преимуществ уникального индекса в качестве ключа соответствия. Это внутреннее соединение, и оптимизатор разумно предполагает, что именно правый поток определит размер реки.
Давайте посмотрим, как оптимизатор трактует те же самые потоки, когда соединение является внешним левым:
SELECT Е. *, S.OLD_SALARY, S.NEW_SALARY
FROM EMPLOYEE E
LEFT JOIN SALARY_HISTORY S
ON S.EMP_NO = E.EMP_NO;
PLAN JOIN (E NATURAL, S INDEX (RDB$FOREIGN21))
В этот раз одна строка будет возвращена для каждой строки правого потока, независимо от того, будет ли существовать соответствующий ключ в управляющем потоке. Размер реки здесь не имеет значения, поскольку внешние соединения однозначно определяют, какая таблица должна быть в левой стороне, чтобы по ней проводить цикл просмотра. Это алгоритм внешнего соединения, который определяет метод доступа, не измеряющий потоки. С таблицей EMPLOYEE В левой части не существует возможности создать внешнее соединение путем просмотра таблицы SALARY_HISTORY с поиском в EMPLOYEE.
Поскольку оптимизатор не делает выбора относительно того порядка, в котором будут соединяться потоки, он просто выбирает наиболее подходящий индекс из SALARY_HISTORY.
В следующем примере размер таблицы не виден при использовании индекса уникального первичного ключа. Таблица DEPARTMENT содержит 21 строку, таблица PROJECT - 6 строк, и оптимизатор выбирает меньший индекс внешнего ключа для оптимизации поиска соответствия по большей таблице: