Оценка кардинальности для> = и> для значения внутришаговой статистики

9

Я пытаюсь понять, как SQL Server пытается оценить предложения «больше чем» и «больше, чем равно» в SQL Server 2014.

Я думаю, что я понимаю оценку мощности, когда она достигает шага, например, если я делаю

    select * from charge where charge_dt >= '1999-10-13 10:47:38.550'

Оценка количества элементов равна 6672, которую можно легко рассчитать как 32 (EQ_ROWS) + 6624 (RANGE_ROWS) + 16 (EQ_ROWS) = 6672 (гистограмма на снимке экрана ниже)

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

Но когда я сделаю

    select * from charge where charge_dt >= '1999-10-13 10:48:38.550' 

(увеличил время до 10:48, так что это не шаг)

оценка 4844,13.

Как это рассчитывается?

jesijesi
источник

Ответы:

9

Единственная трудность состоит в том, чтобы решить, как обрабатывать шаги, которые частично покрываются интервалом предикатов запроса. Все шаги гистограммы, охватываемые диапазоном предикатов, являются тривиальными, как отмечено в вопросе.

Оценщик наследственной мощности

F = доля (от 0 до 1) диапазона шагов, охватываемого предикатом запроса.

Основная идея состоит в том, чтобы использовать F(линейную интерполяцию), чтобы определить, сколько внутришаговых отдельных значений охватывается предикатом. Умножение этого результата на среднее число строк на отдельное значение (при условии равномерности) и добавление шагов равных строк дает оценку количества элементов:

Количество элементов = EQ_ROWS + (AVG_RANGE_ROWS * F * DISTINCT_RANGE_ROWS)

Та же самая формула используется для >и >=в устаревшем CE.

Новая оценка кардинальности

Новый CE немного модифицирует предыдущий алгоритм, чтобы различать >и >=.

Взяв >сначала формулу:

Количество элементов = EQ_ROWS + (AVG_RANGE_ROWS * (F * (DISTINCT_RANGE_ROWS - 1)))

Ибо >=это:

Количество элементов = EQ_ROWS + (AVG_RANGE_ROWS * ((F * (DISTINCT_RANGE_ROWS - 1)) + 1))

+ 1Отражает , что , когда сравнение включает в себя равенство, матч считается (включение предположение).

В примере вопроса Fможно рассчитать как:

DECLARE 
    @Q datetime = '1999-10-13T10:48:38.550',
    @K1 datetime = '1999-10-13T10:47:38.550',
    @K2 datetime = '1999-10-13T10:51:19.317';

DECLARE
    @QR float = DATEDIFF(MILLISECOND, @Q, @K2), -- predicate range
    @SR float = DATEDIFF(MILLISECOND, @K1, @K2) -- whole step range

SELECT
    F = @QR / @SR;

Результат 0,728219019233034 . Подставим это в формулу >=с другими известными значениями:

Количество элементов = EQ_ROWS + (AVG_RANGE_ROWS * ((F * (DISTINCT_RANGE_ROWS - 1)) + 1))
            = 16 + (16,1956 * ((0,728219019233034 * (409 - 1)) + 1))
            = 16 + (16,1956 * ((0,728219019233034 * 408) + 1))
            = 16 + (16.1956 * (297.113359847077872 + 1))
            = 16 + (16.1956 * 298.113359847077872)
            = 16 + 4828.1247307393343837632
            = 4844.1247307393343837632
            = 4844.12473073933 (с плавающей точностью)

Этот результат согласуется с оценкой 4844,13, показанной в вопросе.

Тот же запрос с использованием устаревшего CE (например, с использованием флага трассировки 9481) должен дать оценку:

Количество элементов = EQ_ROWS + (AVG_RANGE_ROWS * F * DISTINCT_RANGE_ROWS)
            = 16 + (16,1956 * 0,728219019233034 * 409)
            = 16 + 4823,72307468722
            = 4839.72307468722

Обратите внимание, что оценка будет такой же >и >=для устаревшего CE.

Пол Уайт 9
источник
4

Формула для оценки строк становится немного глупой, когда фильтр «больше чем» или «меньше чем», но это число, к которому вы можете прийти.

Числа

Используя шаг 193, вот соответствующие цифры:

RANGE_ROWS = 6624

EQ_ROWS = 16

AVG_RANGE_ROWS = 16.1956

RANGE_HI_KEY из предыдущего шага = 1999-10-13 10: 47: 38.550

RANGE_HI_KEY из текущего шага = 1999-10-13 10: 51: 19.317

Значение из предложения WHERE = 1999-10-13 10: 48: 38.550

Формула

1) Найдите мс между двумя клавишами диапазона hi

SELECT DATEDIFF (ms, '1999-10-13 10:47:38.550', '1999-10-13 10:51:19.317')

Результат - 220767 мс.

2) Отрегулируйте количество рядов

Нам нужно найти строки в миллисекунду, но прежде чем мы это сделаем, мы должны вычесть AVG_RANGE_ROWS из RANGE_ROWS:

6624 - 16,1956 = 6607.8044 строки

3) Рассчитать количество строк в мс с заданным количеством строк:

6607.8044 строк / 220767 мс = .0299311 строк в мс

4) Рассчитать мс между значением из предложения WHERE и текущим шагом RANGE_HI_KEY

SELECT DATEDIFF (ms, '1999-10-13 10:48:38.550', '1999-10-13 10:51:19.317')

Это дает нам 160767 мс.

5) Рассчитайте строки в этом шаге на основе строк в секунду:

.0299311 строк / мс * 160767 мс = 4811,9332 строки

6) Помните, как мы вычитали AVG_RANGE_ROWS ранее? Время добавить их обратно. Теперь, когда мы закончили вычисление чисел, связанных со строками в секунду, мы также можем безопасно добавить EQ_ROWS:

4811,9332 + 16,1956 + 16 = 4844,1288

В итоге это наша оценка 4844,13.

Тестирование формулы

Я не смог найти ни одной статьи или поста в блоге о том, почему AVG_RANGE_ROWS вычитается до того, как вычисляются строки в мс. Я был в состоянии подтвердить , что они учитываются в оценке, но только в последнюю миллисекунду - в буквальном смысле.

Используя базу данных WideWorldImporters , я провел некоторое инкрементное тестирование и обнаружил, что уменьшение оценок строк будет линейным до конца шага, где внезапно учитывается 1x AVG_RANGE_ROWS.

Вот мой пример запроса:

SELECT PickingCompletedWhen
FROM Sales.Orders
WHERE PickingCompletedWhen >= '2016-05-24 11:00:01.000000'

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

DBCC SHOW_STATISTICS([sales.orders], '_WA_Sys_0000000E_44CA3770')

Гистограмма для _WA_Sys_0000000E_44CA3770 (последние 3 шага)

Чтобы увидеть, как уменьшаются оценочные строки при приближении к RANGE_HI_KEY, я собирал выборки на протяжении всего шага. Уменьшение является линейным, но ведет себя так, как будто число строк, равное значению AVG_RANGE_ROWS, просто не является частью тренда ... пока вы не нажмете RANGE_HI_KEY, и вдруг они упадут, как списанный непогашенный долг. Вы можете увидеть это на примере данных, особенно на графике.

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

Обратите внимание на устойчивый спад в строках, пока мы не нажмем на RANGE_HI_KEY, а затем на BOOM, из которого неожиданно вычитается последний кусок AVG_RANGE_ROWS. Это легко заметить и на графике.

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

Подводя итог, странная обработка AVG_RANGE_ROWS усложняет вычисление оценок строк, но вы всегда можете согласовать действия CE.

Как насчет экспоненциального отката?

Экспоненциальный откат - это метод, который новый (начиная с SQL Server 2014) оценщик кардинальности использует для получения более точных оценок при использовании нескольких статистических данных за один столбец. Поскольку этот вопрос касался одного столбца stat, он не включает формулу EB.

Дуг Лейн
источник