Як оптимізатор SQL Server оцінює кількість рядків у об'єднаній таблиці?


13

Я виконую цей запит у базі даних AdventureWorks2012 :

SELECT 
    s.SalesOrderID,
    d.CarrierTrackingNumber,
    d.ProductID,
    d.OrderQty
FROM Sales.SalesOrderHeader s 
JOIN Sales.SalesOrderDetail d 
    ON s.SalesOrderID = d.SalesOrderID
WHERE s.CustomerID = 11077

Якщо я дивлюся на прогнозний план виконання, то бачу таке:

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

Початковий пошук індексу (праворуч угорі) використовує індекс IX_SalesOrderHeader_CustomerID та здійснює пошук у прямому знаку 11077. Він має оцінку 2,6192 рядків.

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

Якщо я використовую DBCC SHOW_STATISTICS ('Sales.SalesOrderHeader', 'IX_SalesOrderHeader_CustomerID') WITH HISTOGRAM, це показує, що значення 11077 знаходиться між двома вибіреними ключами 11019 та 11091.

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

Середня кількість різних рядків між 11019 та 11091 становить 2,619718 або округлена до 2,61972, що є значенням оцінених рядків, показаним для пошуку індексу.

Частина, яку я не розумію, - це орієнтовна кількість рядків для кластеризованого індексу для таблиці SalesOrderDetail.

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

Якщо я біжу DBCC SHOW_STATISTICS ('Sales.SalesOrderDetail', 'PK_SalesOrderDetail_SalesOrderID_SalesOrderDetailID'):

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

Тож щільність SalesOrderID (до якої я приєднуюся) становить 3.178134E-05. Це означає, що 1 / 3.178134E-05 (31465) дорівнює кількості унікальних значень SalesOrderID у таблиці SalesOrderDetail.

Якщо в SalesOrderDetail є 31465 унікальних SalesOrderID, то при рівномірному розподілі середня кількість рядків на SalesOrderID становить 121317 (загальна кількість рядків), поділене на 31465. Середнє значення - 3.85561

Отже, якщо передбачувана кількість рядків, які потрібно пройти через цикл, становить 2,61972, а середнє значення, яке потрібно повернути в 3,85561, я думаю, що приблизна кількість рядків буде 2,61972 * 3,85561 = 10,10062.

Але орієнтовна кількість рядків - 11,4867.

Я думаю, що моє розуміння другої оцінки невірно, і, здається, різні цифри вказують на це. Що я пропускаю?

Відповіді:


20

Я думаю, що моє розуміння другої оцінки невірно, і, здається, різні цифри вказують на це. Що я пропускаю?

Використовуючи оцінювач кардинальності SQL Server 2012, вибірковість з'єднання приводить орієнтовну кількість рядків на внутрішній стороні вкладених циклів з'єднання, а не навпаки.

Число 11,4867 виводиться (для відображення в шоу-плані) діленням обчисленої оціночної кардинальності вихідного з'єднання (30.0919) на кількість ітерацій (2.61972). Результат, використовуючи одноточну арифметику з плаваючою комою, становить 11,4867 .

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

У SQL Server 2012 та попередніх версіях вибірковість приєднання (в цілому) оцінюється за допомогою SalesOrderIDгістограм з кожної таблиці (обчислюється для кожного кроку гістограми після вирівнювання меж кроків, використовуючи лінійну інтерполяцію, якщо це необхідно). SalesOrderIDГістограми , пов'язані з SalesOrderHeaderтаблицею також коригуються для масштабування ефекту незалежного CustomerIDфільтра.

Це не означає, що в альтернативному розрахунку, запропонованому у питанні, є щось принципово «неправильне»; це просто робить інший набір припущень. Завжди знайдуться різні способи обчислення або комбінування оцінок для заданої послідовності логічних операцій. Не існує загальної гарантії того, що різні статистичні методи, застосовувані до одних і тих же даних, дадуть однакові відповіді, або що один метод завжди буде кращим за інший. Невідповідності, спричинені застосуванням різних статистичних методів, можуть навіть з’являтися в рамках одного остаточного плану виконання, хоча вони рідко помічаються.

В якості додаткового зауваження, оцінювач кардинальності SQL Server 2014 використовує інший підхід до об'єднання інформації гістограми, скоригованої незалежним фільтром ( "грубе вирівнювання" ), що призводить до різної остаточної оцінки 10,1006 рядків для цього запиту:

Plan for computation:

  CSelCalcExpressionComparedToExpression
  (QCOL: [s].SalesOrderID x_cmpEq QCOL: [d].SalesOrderID)

Loaded histogram for column QCOL: [s].SalesOrderID from stats with id 1
Loaded histogram for column QCOL: [d].SalesOrderID from stats with id 1

Stats collection generated: 

  CStCollJoin(ID=4, **CARD=10.1006** x_jtInner)
      CStCollFilter(ID=3, CARD=2.61972)
          CStCollBaseTable(ID=1, CARD=31465 TBL: Sales.SalesOrderHeader AS TBL: s)
      CStCollBaseTable(ID=2, CARD=121317 TBL: Sales.SalesOrderDetail AS TBL: d)

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

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