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


18

Я намагаюся зрозуміти, чому використання змінної таблиці забороняє оптимізатору використовувати пошук індексів, а потім шукати закладку порівняно зі скануванням індексу.

Наповнення таблиці:

CREATE TABLE dbo.Test 
(
    RowKey INT NOT NULL PRIMARY KEY, 
    SecondColumn CHAR(1) NOT NULL DEFAULT 'x',
    ForeignKey INT NOT NULL 
) 

INSERT dbo.Test 
(
    RowKey, 
    ForeignKey
) 
SELECT TOP 1000000 
    ROW_NUMBER() OVER (ORDER BY (SELECT 0)),
    ABS(CHECKSUM(NEWID()) % 10)     
FROM sys.all_objects s1
CROSS JOIN sys.all_objects s2 

CREATE INDEX ix_Test_1 ON dbo.Test (ForeignKey) 

Наповніть змінну таблиці за допомогою одного запису та спробуйте знайти первинний ключ та другий стовпець шляхом пошуку в стовпці із зовнішнім ключем:

DECLARE @Keys TABLE (RowKey INT NOT NULL) 

INSERT @Keys (RowKey) VALUES (10)

SELECT 
    t.RowKey,
    t.SecondColumn
FROM
    dbo.Test t 
INNER JOIN 
    @Keys k
ON
    t.ForeignKey = k.RowKey

Нижче наведено план виконання:

введіть тут опис зображення

Тепер той самий запит, використовуючи замість таблиці темп:

CREATE TABLE #Keys (RowKey INT NOT NULL) 

INSERT #Keys (RowKey) VALUES (10) 

SELECT 
    t.RowKey,
    t.SecondColumn
FROM
    dbo.Test t 
INNER JOIN 
    #Keys k
ON
    t.ForeignKey = k.RowKey

Цей план запитів використовує пошук пошуку та закладки:

введіть тут опис зображення

Чому оптимізатор готовий шукати закладки за допомогою таблиці temp, але не змінною таблиці?

Таблиця змінної використовується в цьому прикладі для представлення даних, що надходять через визначений користувачем тип таблиці в збереженій процедурі.

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

SQL Fiddle

Додавання OPTION (RECOMPILE)не змінило поведінку. У UDDT є первинний ключ.

@@VERSION є SQL Server 2008 R2 (SP2) - 10.50.4042.0 (X64) (збірка 7601: пакет оновлень 1) (Hypervisor)

Відповіді:


15

Причина такої поведінки полягає в тому, що SQL Server не може визначити, скільки рядків буде відповідати ForeignKey, оскільки немає індексу з RowKey як провідним стовпцем (він може вивести це зі статистичних даних в таблиці #temp, але ті не існує для змінних таблиць / UDTT), тому вона складає оцінку 100 000 рядків, що краще обробляти при скануванні, ніж пошук + пошук. На той час, коли SQL Server зрозуміє, що існує лише один ряд, вже пізно.

Ви можете побудувати свій UDTT по-різному; у більш сучасних версіях SQL Server ви можете створювати вторинні індекси на змінних таблиць, але цей синтаксис недоступний у R2 2008 року.

BTW ви можете отримати поведінку пошуку (принаймні, в моїх обмежених випробуваннях), якщо ви спробуєте уникнути растрової карти / зонда, натякаючи на вкладені вкладені петлі:

DECLARE @Keys TABLE (RowKey INT PRIMARY KEY); -- can't hurt

INSERT @Keys (RowKey) VALUES (10);

SELECT 
     t.RowKey
    ,t.SecondColumn
FROM
    dbo.Test t 
INNER JOIN 
    @Keys k
ON
    t.ForeignKey = k.RowKey
    OPTION (LOOP JOIN);

Цю хитрість я дізнався від Пола Уайта кілька років тому. Звичайно, вам слід бути обережними щодо внесення будь-яких підказів про приєднання до виробничого коду - це може не вдатися, якщо люди внесуть зміни в основні об'єкти, і конкретний тип приєднання вже не можливий або вже не є найбільш оптимальним.

Для складніших запитів, і коли ви переходите на SQL Server 2012 або вище, можливо, прапор сліду 2453 може допомогти. Цей прапор не допоміг у цьому простому приєднанні. І ті ж самі відмови від відповідальності - це лише альтернативна річ, яку ви, як правило, не обходите без тотальної документації та суворих процедур тестування регресії.

Крім того, Service Pack 1 давно не підтримує, ви повинні отримати на Service Pack 3 + MS15-058 .


3

Таблиці змінних та тимчасових таблиць обробляються по-різному різними способами. Тут є чудова відповідь з великою кількістю конкретики щодо того, де вони різні.

Зокрема, у вашому випадку я б здогадався, що винуватцем того, що темп-таблиці можуть створювати додаткові статистичні дані та паралельні плани, тоді як змінні таблиць мають більш обмежену статистику (відсутність статистики на рівні стовпців), і немає паралельних планів.

Вам може бути краще скинути змінну таблиці в таблицю темпів протягом тривалості збереженої процедури.

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