Чому мій ЗАМОВЛЕННЯ за сортуванням двох таблиць перед ВИКЛЮЧЕННЯ (повільно), а не після (швидко)?


12

Головоломка оптимізатора запитів SQL-сервера 2008 R2

У нас є дві таблиці, обидві містять 9 мільйонів рядків. 70 000 рядів різні, інші ж.

Це швидко, 13 секунд,

select * from bigtable1
except select * from similar_bigtable2

Це сортує вихід, а також швидко, 13 секунд,

select * into #q from bigtable1
except select * from similar_bigtable2
select * from #q order by sort_column

Хоча це надзвичайно повільно:

;with q as (
    select * from bigtable1
    except select * from similar_bigtable2
)
select * from q order by sort_column

І навіть "хитрість", яку я іноді використовую, щоб натякнути SQL Server, що йому потрібно перерахувати певну частину запиту, перш ніж він рухається далі, не працює і призводить до повільного запиту:

;with q as (
    select top 100 percent * from bigtable1
    except select * from similar_bigtable2
)
select * from q order by sort_column

Дивлячись на плани запитів, причину не важко знайти:

План запитів План запиту з ЗАМОВЛЕННЯ ПО

SQL Server розміщує два різновиди в 9 мільйонів рядків перед хешмачем, хоча я вважаю за краще, щоб після хешмату було додано лише один вид у 70 000 рядків .

Отже, питання: як я можу доручити оптимізатору запитів зробити це?


3
Він не сортує перед хешмачем, він сортує, а потім робить об'єднання об'єднання (не хеш-з'єднання). Можливо, є натяк на те, щоб змусити хеш-з'єднання (або запобігти злиття-приєднання)?
Тіло

3
Схоже, оптимізатор запитів SQL Server визначив, що сортування даних було вигідним, щоб воно могло використовувати набагато швидше об'єднання об'єднань (яке працює лише для відсортованих даних), а не набагато повільніше Hash Match Join або Nested Loop Join ....
marc_s

9
Ви спробували альтернативи EXCEPT(наприклад OUTER JOIN)? Я розумію, що синтаксис є менш зручним, але ви, можливо, там зможете краще зіграти з підказками / підключеннями (або, можливо, не потрібно). Альтернатива, яку ви зараз використовуєте (спочатку в таблицю #temp), є крайнім способом вирішення, але в деяких випадках - єдиний спосіб змусити оптимізатор повністю розділити дві частини запиту так, як вам потрібно.
Аарон Бертран

Відповіді:


1

Основна відмінність цих двох планів запитів насправді полягає в різниці Hash Match і Merge Join. Hash Match є більш ефективним, і як ви бачите, запит працює швидше у варіанті 1 (не використовуючи CTE).

CTE - чудовий інструмент, але він, здається, не ефективний у двох випадках, складні предикати або не унікальний ключ батьків / дочір. У вашому випадку немає унікального ключа, і SQL-сервер повинен спочатку сортувати набори даних, щоб мати змогу виконати вашу вимогу. Перегляньте посилання нижче, яке розповість вам більше про це питання: http://blogs.msdn.com/b/sqlcat/archive/2011/04/28/optimize-recursive-cte-query.aspx

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


0

Спробуйте це, краще?

select * from
(
    select * from bigtable1
    except 
    select * from similar_bigtable2
) t
order by sort_column

0

Це не ідеальне рішення, але якщо ви не в змозі структурувати tsql для створення ефективного плану, ви можете встановити керівництво щодо плану, щоб змусити план, який ви хочете. Це буде означати, що якщо буде доступний більш ефективний план, SQL його не розгляне, але це варіант.

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