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


40

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

Тестова установка

Сценарій налаштування, включаючи деякі реалістичні статистичні дані:

DROP TABLE IF EXISTS #left;
DROP TABLE IF EXISTS #right;

CREATE TABLE #left (
    a       char(4) NOT NULL,
    b       char(2) NOT NULL,
    c       varchar(13) NOT NULL,
    d       bit NOT NULL,
    e       char(4) NOT NULL,
    f       char(25) NULL,
    g       char(25) NOT NULL,
    h       char(25) NULL
    --- and a few other columns
);

CREATE UNIQUE CLUSTERED INDEX IX ON #left (a, b, c, d, e, f, g, h)

UPDATE STATISTICS #left WITH ROWCOUNT=63800000, PAGECOUNT=186000;

CREATE TABLE #right (
    a       char(4) NOT NULL,
    b       char(2) NOT NULL,
    c       varchar(13) NOT NULL,
    d       bit NOT NULL,
    e       char(4) NOT NULL,
    f       char(25) NULL,
    g       char(25) NOT NULL,
    h       char(25) NULL
    --- and a few other columns
);

CREATE CLUSTERED INDEX IX ON #right (a, b, c, d, e, f, g, h)

UPDATE STATISTICS #right WITH ROWCOUNT=55700000, PAGECOUNT=128000;

Запрошення

Коли я приєднуюся до цих двох таблиць на клавішних клавішах, я очікую приєднання МНОГО одного до багатьох, наприклад:

SELECT *
FROM #left AS l
LEFT JOIN #right AS r ON
    l.a=r.a AND
    l.b=r.b AND
    l.c=r.c AND
    l.d=r.d AND
    l.e=r.e AND
    l.f=r.f AND
    l.g=r.g AND
    l.h=r.h
WHERE l.a='2018';

Цей план запитів я хочу:

Це я хочу.

(Незважаючи на попередження, вони мають відношення до підробленої статистики.)

Однак якщо я зміню порядок стовпців навколо приєднання, так:

SELECT *
FROM #left AS l
LEFT JOIN #right AS r ON
    l.c=r.c AND     -- used to be third
    l.a=r.a AND     -- used to be first
    l.b=r.b AND     -- used to be second
    l.d=r.d AND
    l.e=r.e AND
    l.f=r.f AND
    l.g=r.g AND
    l.h=r.h
WHERE l.a='2018';

... це відбувається:

План запитів після зміни заявленого порядку стовпців при з'єднанні.

Очевидно, оператор Sort упорядковує потоки відповідно до заявленого порядку з'єднання, тобто c, a, b, d, e, f, g, hдодає операцію блокування до мого плану запитів.

Речі, на які я дивився

  • Я спробував змінити стовпці на NOT NULLоднакові результати.
  • Оригінальну таблицю було створено за допомогою ANSI_PADDING OFF, але її створення за допомогою ANSI_PADDING ONцього плану не впливає.
  • Я спробував INNER JOINзамість цього LEFT JOIN, без змін.
  • Я виявив це на SP2 Enterprise 2014 року, створив репрограму для розробника 2017 року (поточний CU).
  • Видалення пункту WHERE з провідного стовпця індексу генерує хороший план, але це на зразок впливає на результати .. :)

Нарешті, ми переходимо до питання

  • Це навмисно?
  • Чи можу я усунути сортування, не змінюючи запит (який є кодом постачальника, так що я дійсно краще не ...). Я можу змінити таблицю та індекси.

Відповіді:


28

Це навмисно?

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

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

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

Наприклад, сортування часто можна уникнути, просто порівнявши вимогу замовлення (наприклад, верхнього рівня ORDER BY) до існуючого індексу. Тривіально у вашому випадку це може означати додавання, ORDER BY l.a, l.b, l.c, l.d, l.e, l.f, l.g, l.h;але це надмірне спрощення (і неприйнятне, оскільки ви не хочете змінювати запит).

Більш загально, кожна група нагадувань може бути пов'язана з необхідними або бажаними властивостями, які можуть включати впорядкування вводу. Коли немає очевидних причин примусового виконання певного замовлення (наприклад, для задоволення ORDER BYчи забезпечення правильних результатів від фізичного оператора, залежного від замовлення), є елемент «удачі». Я писав більше про специфіку цього, оскільки це стосується об'єднання об'єднань (в режимі об'єднання або приєднання) у запобіганні сортуванням із об'єднанням об'єднань . Значна частина цього виходить за межі підтримуваної поверхні поверхні виробу, тому сприймайте його як інформаційне та підлягайте змінам.

У вашому конкретному випадку, так, ви можете скорегувати індексацію, оскільки jadarnel27 пропонує уникати подібних варіантів ; хоча мало причин насправді віддавати перевагу об'єднанню тут. Ви також можете натякнути на вибір між фізичним об'єднанням хешу чи циклу, OPTION(HASH JOIN, LOOP JOIN)використовуючи Посібник із плану, не змінюючи запит, залежно від ваших знань про дані, та компроміс між найкращою, найгіршою та середньою ефективністю.

Нарешті, як цікавість, зауважте, що сортів можна уникнути простим ORDER BY l.b, ціною потенційно менш ефективного злиття багатьох до багатьох, що поєднуються bнаодинці зі складним залишком. Я згадую це здебільшого як ілюстрацію взаємодії між функціями оптимізатора, про які я згадував раніше, і способом розповсюдження вимог верхнього рівня.


19

Чи можу я усунути сортування, не змінюючи запит (який є кодом постачальника, так що я дійсно краще не ...). Я можу змінити таблицю та індекси.

Якщо ви можете змінити індекси, то зміна порядку індексу на, #rightщоб відповідати порядку фільтрів у з'єднанні, видаляє сортування (для мене):

CREATE CLUSTERED INDEX IX ON #right (c, a, b, d, e, f, g, h)

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

Це навмисно?

Дивлячись на вихід із деяких дивних прапорів слідів , є цікава різниця в остаточній структурі пам'яті:

скріншот остаточної структури нагадувань для кожного запиту

Як ви бачите в "Root Group" вгорі, обидва запити мають можливість використовувати об'єднання об'єднань як основну фізичну операцію для виконання цього запиту.

Хороший запит

Об'єднання без сортування визначається варіантом 1 групи 29 та варіантом 1 групи 31 (кожен з яких - це сканування діапазону за включеними індексами). Він фільтрується за групою 27 (не показано), що є серією логічних операцій порівняння, які фільтрують з'єднання.

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

Той, хто має сортування, визначається за допомогою (нових) варіантів 3, які має кожна з цих двох груп (29 і 31). Варіант 3 здійснює фізичне сортування за результатами сканування діапазону, згаданого раніше (варіант 1 кожної з цих груп).

Чому?

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

Я можу лише зробити висновок, що:

  • це помилка (або швидше обмеження) в алгоритмі пошуку оптимізатора
    • зміна індексів та приєднання лише до 5 клавіш видаляє сортування для другого запиту (всі 6, 7 та 8 клавіш мають вид).
    • Це означає, що пошуковий простір з 8 клавішами настільки великий, що оптимізатор просто не встигає визначити несортивне рішення як життєздатний варіант, перш ніж він достроково припиняється із причиною "достатньо хорошого плану".
    • мені здається трохи баггі, що порядок умов приєднання сильно впливає на процес пошуку оптимізатора, але насправді це трохи над головою
  • сортування потрібно для того, щоб забезпечити правильність результатів
    • це здається малоймовірним, оскільки запит може запускатися без сортування, коли менше клавіш, або ж ключі вказані в іншому порядку

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


1
Я вважаю, що ваш коментар щодо місця пошуку насправді є таким. щоб використовувати лише індекси, оптимізатор повинен переконатися, що вони достатні для умов, за останні 5 клавіш є занадто багато можливостей перевірити, перш ніж він повинен відпасти назад. Мені буде цікаво, якби були перераховані всі комбінації замовлень запиту, скільки оптимізатора вдасться досягти проти vs
Mr.Mindor

І так, непослідовність дійсно здається непомітною, але, ймовірно, повністю залежить від алгоритму, який використовується для перевірки індексів. Якби всі комбінації були протестовані, ви, ймовірно, зможете побачити схему в результатах та визначити, який алгоритм використовується. Б'юсь у заклад, це написано оптимально для більш типових випадків використання. Може існувати альтернатива, яка змогла б надійно знайти рішення 8 клавіш протягом часу, але це повільніше, ніж поточне рішення, коли менше, ніж скажімо, 3-4 клавіші.
Містер Міндор
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.