Чому цей запит не використовує котушку індексу?


23

Я задаю це запитання, щоб краще зрозуміти поведінку оптимізатора і зрозуміти межі навколо котушок індексу. Припустимо, я поклав цілі числа від 1 до 10000 у купу:

CREATE TABLE X_10000 (ID INT NOT NULL);
truncate table X_10000;

INSERT INTO X_10000 WITH (TABLOCK)
SELECT TOP 10000 ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;

І змусити вкладений цикл з'єднатись із MAXDOP 1:

SELECT *
FROM X_10000 a
INNER JOIN X_10000 b ON a.ID = b.ID
OPTION (LOOP JOIN, MAXDOP 1);

Це досить недружні дії, спрямовані на SQL Server. Вкладений цикл з'єднання часто не є хорошим вибором, коли в обох таблицях немає відповідних індексів. Ось план:

поганий запит

На моїй машині запит займає 13 секунд, із 100000000 рядків, зібраних із котушки таблиці. Однак я не бачу, чому запит має бути повільним. Оптимізатор запитів має можливість створювати індекси під час руху через котушки індексів . Цей запит, схоже, був би ідеальним кандидатом на індексну котушку.

Наступний запит повертає ті ж результати, що і перший, має індексну котушку і закінчується менше ніж за секунду:

SELECT *
FROM X_10000 a
CROSS APPLY (SELECT TOP (9223372036854775807) b.ID FROM X_10000 b WHERE a.ID = b.ID) ca
OPTION (LOOP JOIN, MAXDOP 1);

обхід 1

Цей запит також має індексну котушку і закінчується менше ніж за секунду:

SELECT *
FROM X_10000 a
INNER JOIN X_10000 b ON a.ID >= b.ID AND a.ID <= b.ID
OPTION (LOOP JOIN, MAXDOP 1);

обхід 2

Чому оригінальний запит не містить котушки індексу? Чи є якийсь набір документально підтверджених або недокументованих натяків чи слідів, які дадуть йому індексну котушку? Я знайшов це пов’язане питання , але воно не відповідає повністю на моє запитання, і я не можу заставити загадковий слід прапора для роботи над цим запитом.

Відповіді:


20

Як відомо, пошук оптимізатора не є вичерпним. Він пробує речі, які мають сенс у контексті, і які часто виплачують дивіденди за реальними запитами. Примусовий приєднання циклу між двома стовпчиками невкладених таблиць купи не є таким сценарієм. Однак, ось деякі деталі:

SQL Server любить трансформуватися, застосовується до об'єднань рано, тому що він знає більше хитрощів з приєднанням. Пізніше він може дослідити перетворення з'єднання назад у додаток. Різниця між цими двома корелюються параметрами (зовнішні посилання). Застосовує сенс, коли на внутрішній стороні є відповідний індекс. У вашому прикладі немає індексів, тому оптимізатор не переконує вивчити переклад на додаток.

Просте з'єднання (не застосовується) має предикат приєднання на операторі об'єднання замість зовнішніх посилань. Оптимізація котушки для не застосовується, як правило, ледача котушка таблиці, оскільки на внутрішній стороні немає присудка, лише при з'єднанні.

Оптимізатор не розглядає можливість створення індексу на льоту, щоб включити додаток; скоріше послідовність подій, як правило, зворотна: перетворення застосовувати, оскільки існує хороший індекс.

Іноді ви можете заохочувати застосувати, а не приєднуватись, використовуючи APPLYсинтаксис у своєму запиті. Недокументований прапор сліду 9114 може допомогти у цьому, відмовивши оптимізатора від перекладу логічного застосування на фронт з'єднання. Наприклад:

SELECT * 
FROM dbo.X_1000 AS a
CROSS APPLY (SELECT * FROM dbo.X_1000 AS b WHERE b.ID = a.ID) AS b
OPTION (QUERYTRACEON 9114);

План котушки

Індексна котушка сприятлива для застосування, оскільки зовнішня посилання означає, що вибір застосовується на внутрішній стороні з'єднання. Ви часто будете бачити це через, SelToIndexOnTheFlyале існують інші шляхи. Дивіться мою статтю Котушка індексу Eager та оптимізатор .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.