Чому оператор Concatenation оцінює менше рядків, ніж його введення?


20

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

Однак складається оцінка ~238 million rows, що призводить до неоптимальної Sort/ Stream Aggregateстратегії, яка розсипає сотні ГБ даних на tempdb. Логічно послідовна оцінка в цьому випадку призвела б до Hash Aggregateусунення розливу та суттєво покращеного виконання запитів.

Це помилка в SQL Server 2014? Чи є обґрунтовані обставини, за яких оцінка, нижча за вхідні, може бути розумною? Які обхідні шляхи можуть бути доступні?

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

Ось повний план запитів (анонімізований). У мене немає доступу до системного адміністратора до цього сервера для того, щоб надати виходи з QUERYTRACEON 2363подібних прапорів слідів, але можливо, я можу отримати ці результати від адміністратора, якщо вони будуть корисні.

База даних знаходиться на рівні сумісності 120, а тому використовує новий Оцінювач кардинальності SQL Server 2014.

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

Відповіді:


21

Щоб цитувати Кемпбелла Фрейзера на цьому елементі Connect :

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

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

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

Все, що було сказано, були внесені деякі докладні зміни до нового оцінювача кардинальності (CE), введеного в SQL Server 2014, що робить це дещо рідше, ніж це було у випадку з оригінальним СЕ.

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

Анонімізований план ускладнює оцінку деталей, але я також уважно переглянув битові карти, щоб побачити, чи є вони різновидами "оптимізованих" (Opt_Bitmap) або пост-оптимізаційних (Bitmap). Я також підозріло ставляться до Фільтри.

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


7

Побудова, очевидно, досить простого тестового шару на SQL Server 2012 (11.0.6020), дозволяє мені відтворити план із двома запитами, відповідніми хешу, об'єднаним через UNION ALL. Мій тестовий шар не відображає неправильну оцінку, яку ви бачите. Можливо , це є проблемою SQL Server 2014 CE.

Я отримую оцінку в 133,785 рядків за запитом, який фактично повертає 280 рядків, однак цього слід очікувати, як ми побачимо далі вниз:

IF OBJECT_ID('dbo.Union1') IS NOT NULL
DROP TABLE dbo.Union1;
CREATE TABLE dbo.Union1
(
    Union1_ID INT NOT NULL
        CONSTRAINT PK_Union1
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
    , Union1_Text VARCHAR(255) NOT NULL
    , Union1_ObjectID INT NOT NULL
);

IF OBJECT_ID('dbo.Union2') IS NOT NULL
DROP TABLE dbo.Union2;
CREATE TABLE dbo.Union2
(
    Union2_ID INT NOT NULL
        CONSTRAINT PK_Union2
        PRIMARY KEY CLUSTERED
        IDENTITY(2,2)
    , Union2_Text VARCHAR(255) NOT NULL
    , Union2_ObjectID INT NOT NULL
);

INSERT INTO dbo.Union1 (Union1_Text, Union1_ObjectID)
SELECT o.name, o.object_id
FROM sys.objects o;

INSERT INTO dbo.Union2 (Union2_Text, Union2_ObjectID)
SELECT o.name, o.object_id
FROM sys.objects o;
GO

SELECT *
FROM dbo.Union1 u1
    INNER HASH JOIN sys.objects o ON u1.Union1_ObjectID = o.object_id
UNION ALL
SELECT *
FROM dbo.Union2 u2
    INNER HASH JOIN sys.objects o ON u2.Union2_ObjectID = o.object_id;

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

Джо Сак цікаво прочитав про це тут .

Для a UNION ALL, можна сказати, що ми точно побачимо загальну кількість рядків, повернутих кожним компонентом об'єднання, однак, оскільки SQL Server використовує оцінки рядків для двох компонентів UNION ALL, ми бачимо, що він додає загальну оціночну кількість рядків з обох запити, щоб створити оцінку для оператора конкатенації.

У моєму прикладі вище, орієнтовна кількість рядків для кожної частини значень UNION ALLстановить 66,8927, що при підсумовуванні дорівнює 133,785, що ми бачимо для передбачуваної кількості рядків для оператора конкатенації.

Фактичний план виконання запиту на об'єднання вище виглядає так:

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

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

Я б спробував отримати висновок від сліду 2363 тощо, як це рекомендовано у публікації Пола Уайта, яку ви показуєте у своєму питанні. Крім того, ви можете спробувати скористатися OPTION (QUERYTRACEON 9481)в запиті для повернення до версії 70 CE, щоб побачити, чи "це" вирішує проблему.


1
Спасибі. Я, безумовно, бачив, що "причина полягає у відсутності статистичних даних щодо двох приєднаних результатів, які є об'єднаними", мають великий вплив на наступні приєднання або агрегації (які відбуваються після Союзу). На моєму досвіді SQL 2014 насправді справляється з цим краще, ніж SQL 2012. Ось простий тестовий сценарій, який я використовував раніше, наприклад: gist.github.com/anonymous/1497112d8b25ab8fb782a04569959c68 Однак я не думаю, що оператору конкатенації потрібна така сама інформація про розподіл значень, що приєднання може знадобитися.
Джефф Паттерсон

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