Рассмотрим простой запрос и план выполнения AdventureWorks, показанные ниже. Запрос содержит предикаты, связанные с AND
. Оценка кардинальности оптимизатора составляет 41 211 строк:
-- Estimate 41,211 rows
SELECT COUNT_BIG(*)
FROM Production.TransactionHistory AS TH
WHERE
TH.TransactionID BETWEEN 100000 AND 168336
AND TH.TransactionDate BETWEEN '2007-09-01' AND '2008-03-13';
Использование статистики по умолчанию
Учитывая только статистику по одному столбцу, оптимизатор производит эту оценку, оценивая количество элементов для каждого предиката в отдельности и умножая полученную селективность вместе. Эта эвристика предполагает, что предикаты полностью независимы.
Разделение запроса на две части упрощает вычисление:
-- Estimate 68,336.4 rows
SELECT COUNT_BIG(*)
FROM Production.TransactionHistory AS TH
WHERE
TH.TransactionID BETWEEN 100000 AND 168336;
Таблица истории транзакций содержит в общей сложности 113 443 строки, поэтому оценка 68 336,4 представляет селективность 68336,4 / 113443 = 0,60238533 для этого предиката. Эта оценка получается с использованием информации гистограммы для TransactionID
столбца и значений констант, указанных в запросе.
-- Estimate 68,413 rows
SELECT COUNT_BIG(*)
FROM Production.TransactionHistory AS TH
WHERE
TH.TransactionDate BETWEEN '2007-09-01' AND '2008-03-13';
Этот предикат имеет оценочную селективность 68413,0 / 113443 = 0,60306056 . Опять же, он рассчитывается на основе постоянных значений предиката и гистограммы TransactionDate
объекта статистики.
Предполагая, что предикаты полностью независимы, мы можем оценить селективность двух предикатов вместе, умножив их вместе. Окончательная оценка количества элементов получается умножением результирующей селективности на 113 443 строки в базовой таблице:
0,6038533 * 0,60306056 * 113443 = 41210,987
После округления это 41 211 оценка, показанная в исходном запросе (оптимизатор также использует внутреннюю математику с плавающей запятой).
Не очень хорошая оценка
TransactionID
И TransactionDate
столбцы имеют тесную корреляцию данных AdventureWorks набор (как монотонно возрастающие ключи и столбцы дат часто делают). Эта корреляция означает, что предположение о независимости нарушается. Как следствие, план запроса после выполнения показывает 68 095 строк, а не 41 411:
Флаг трассировки 4137
Включение этого флага трассировки изменяет эвристику, используемую для объединения предикатов. Вместо полной независимости оптимизатор считает, что селективности двух предикатов достаточно близки, чтобы их можно было коррелировать:
-- Estimate 68,336.4
SELECT COUNT_BIG(*)
FROM Production.TransactionHistory AS TH
WHERE
TH.TransactionID BETWEEN 100000 AND 168336
AND TH.TransactionDate BETWEEN '2007-09-01' AND '2008-03-13'
OPTION (QUERYTRACEON 4137);
Напомним, что TransactionID
один только предикат оценил 68,336,4 строки, а TransactionDate
один только предикат оценил 68,413 строк. Оптимизатор выбрал нижнюю из этих двух оценок, а не умножающую селективность.
Конечно, это просто другая эвристика, но она может помочь улучшить оценки для запросов с коррелированными AND
предикатами. Каждый предикат рассматривается для возможной корреляции, и есть другие корректировки, сделанные, когда AND
задействовано много предложений, но этот пример служит для демонстрации его основ.
Статистика по нескольким столбцам
Это может помочь в запросах с корреляциями, но информация гистограммы по-прежнему основана исключительно на ведущем столбце статистики. Таким образом, следующие статистические данные-кандидаты отличаются по важности:
CREATE STATISTICS
[stats Production.TransactionHistory TransactionID TransactionDate]
ON Production.TransactionHistory
(TransactionID, TransactionDate);
CREATE STATISTICS
[stats Production.TransactionHistory TransactionDate TransactionID]
ON Production.TransactionHistory
(TransactionDate, TransactionID);
Взяв только один из них, мы видим, что единственная дополнительная информация - это дополнительные уровни плотности «все». Гистограмма все еще содержит только подробную информацию о TransactionDate
столбце.
DBCC SHOW_STATISTICS
(
'Production.TransactionHistory',
'stats Production.TransactionHistory TransactionDate TransactionID'
);
С этой многостолбцовой статистикой на месте ...
SELECT COUNT_BIG(*)
FROM Production.TransactionHistory AS TH
WHERE
TH.TransactionID BETWEEN 100000 AND 168336
AND TH.TransactionDate BETWEEN '2007-09-01' AND '2008-03-13';
... план выполнения показывает оценку, точно такую же, как тогда, когда была доступна только статистика по одному столбцу:
Statistics objects on multiple columns also store statistical information about the correlation of values among the columns