Следующий запрос занимает около 10 секунд для завершения таблицы с 12k записей
select top (5) *
from "Physician"
where "id" = 1 or contains("lastName", '"a*"')
Но если я изменю предложение where на
where "id" = 1
или
where contains("lastName", '"a*"')
Он вернется мгновенно.
Оба столбца проиндексированы, а столбец lastName также полнотекстовый.
CREATE TABLE Physician
(
id int identity NOT NULL,
firstName nvarchar(100) NOT NULL,
lastName nvarchar(100) NOT NULL
);
ALTER TABLE Physician
ADD CONSTRAINT Physician_PK
PRIMARY KEY CLUSTERED (id);
CREATE NONCLUSTERED INDEX Physician_IX2
ON Physician (firstName ASC);
CREATE NONCLUSTERED INDEX Physician_IX3
ON Physician (lastName ASC);
CREATE FULLTEXT INDEX
ON "Physician" ("firstName" LANGUAGE 0x0, "lastName" LANGUAGE 0x0)
KEY INDEX "Physician_PK"
ON "the_catalog"
WITH stoplist = off;
Вот план выполнения
В чем может быть проблема?
sql-server
sql-server-2008-r2
full-text-search
Хуман Валибейги
источник
источник
Ответы:
Ваш план выполнения
Рассматривая план запроса, мы видим, что один индекс затрагивается для обслуживания двух операций фильтрации.
Проще говоря, благодаря оператору TOP была поставлена цель строки. Гораздо больше информации и предпосылки на цели ряда можно найти здесь
Из того же источника:
Вся таблица прощупывается в фильтрах с использованием левого полусоединения, у которого установлена цель строки, в надежде вернуть 5 строк как можно быстрее и эффективнее.
Этого не происходит, что приводит к множеству итераций по TVF .Fulltextmatch.
Воссоздание
Исходя из вашего плана , я смог несколько воссоздать вашу проблему:
Выполнение запроса
Результаты в план запроса, сопоставимый с вашим:
В приведенном выше примере B не существует в полнотекстовом индексе. В результате от параметра и данных зависит, насколько эффективным может быть план запроса.
Лучшее объяснение этому можно найти в Строковых Целях, Часть 2: Полу-соединения Пола Уайта.
Например, изменив предикат, чтобы результаты были найдены намного раньше (в начале сканирования).
where "id" = 124
получает устранен за счет полнотекстового индекса предиката уже возвращаются 5 строк, удовлетворяющийTOP()
предикату.Результаты показывают это также
И TVF казни:
Вставка некоторых новых строк
Выполнение запроса для поиска этих предыдущих вставленных строк
Это снова приводит к слишком большому количеству итераций почти по всем строкам, чтобы вернуть последнее, но одно найденное значение.
Разрешающая
При удалении цели строки с помощью traceflag 4138
Оптимизатор использует шаблон соединения ближе к реализации a
UNION
, в нашем случае это выгодно, так как он толкает предикаты вниз к их соответствующим поискам кластеризованного индекса, и не использует левый полусоединение оператора goals строки.Другой способ написать это, не используя вышеупомянутый traceflag:
С полученным планом запроса:
где полнотекстовая функция применяется непосредственно
Как примечание, для op, исправление оптимизатора запросов traceflag 4199 решило его проблему. Он реализовал это, добавив
OPTION(QUERYTRACEON(4199))
к запросу. Я не смог воспроизвести это поведение на моем конце. Это исправление содержит оптимизацию полусоединения:Источник
дополнительный
Во время оптимизации на основе затрат оптимизатор также может добавить катушку индекса в план выполнения, реализованный
LogOp_Spool Index on fly Eager
(или физическим аналогом)Это делает это с моим набором данных для,
TOP(3)
но не дляTOP(2)
Источник
С предикатом поиска, примененным к этому индексу, требуется спул:
источник