Есть запрос:
Код: Выделить всё
Select L.RECID
From L
Where L.VALID IN (
Select ID
From C
Where C.FValue = 'test'
)
Код: Выделить всё
PLAN (L NATURAL)
PLAN (C_FLD_WORDS INDEX (RDB$PRIMARY75))
L:
Количество записей: 2 846 058
Статистика по внешенему ключу на поле ValID: 0.00000785
C:
Количество записей: 127 238
Статистика по первичному ключу на поле ID: 0.00000785
Статистика по индексу на поле FValue: 0.00000785
Таким образом, что мы видим:
Идет натуральный перебор по таблице L. И для каждого значения L происходит индексный поиск по результату выполнения подзапроса.
Подзапрос возвращает одно значение. Таким образом, сервер выполняет перебор по почти 3 миллионам значений и для каждого из них делает поиск по индексированному набору данных как результат выполнения подзапроса (этот набор содержит в данном случае одну запись).
Отсюда очевидно, что было бы намного эффективнее выполнять данный сложный запрос в обратном порядке. То-есть, вначале выполнить внутренний подзапрос, и делать натуральный перебор по результату этого подзапроса, и для каждого значения этого результата выполнять индексный поиск по таблице L. Таким образом, был бы натуральный перебор по результату выполнения подзапроса (который содержит одну запись в данному случае), и для каждой этой записи производился бы индексный поиск по таблице L. И в этом случае мы бы имели вместо чтения 7 миллионов записей чтение около 120 тысяч записей.
Оптимизатор принимает дивное решение о натуральном поиске по L еще и потому, что таблица L содержит почти 3 миллиона записей, а таблица C - около 120 тысяч, и поэтому натуральный перебор по C был бы более логичный.
Отсюда собственно вопрос. Почему собственно оптимизатор работает таким вот образом, и, что более важно, как заставить его делать натуральный перебор по подзапросу, и для каждой записи подзапроса выполнять индексный поиск по таблице L? Помогите пожалуйста, очень нужно.
Данные о среде выполнения:
Версия сервера: Firebird 1.5.1
Версия ОС: MS Windows 2000 server
Скрипт создания таблиц:
Код: Выделить всё
CREATE DOMAIN TYPE_ID AS
INTEGER
NOT NULL
-- C Table --
CREATE TABLE C_FLD_WORDS (
ID TYPE_ID /* TYPE_ID = INTEGER NOT NULL */,
FVALUE VARCHAR(30)
);
ALTER TABLE C_FLD_WORDS ADD PRIMARY KEY (ID); -- RDB$PRIMARY75
CREATE INDEX C_FLD_WORDS_VAL ON C_FLD_WORDS (FVALUE);
-- L Table --
CREATE TABLE L_FLD_WORDS (
RECID TYPE_ID /* TYPE_ID = INTEGER NOT NULL */,
VALID TYPE_ID /* TYPE_ID = INTEGER NOT NULL */
);
ALTER TABLE L_FLD_WORDS ADD FOREIGN KEY (VALID) REFERENCES C_FLD_WORDS (ID); -- Индекс RDB$FOREIGN228