Оценка мощности для оператора LIKE (локальные переменные)

24

У меня сложилось впечатление, что при использовании LIKEоператора во всех операциях оптимизации для неизвестных сценариев как устаревшие, так и новые CE используют оценку в 9% (при условии, что доступны соответствующие статистические данные и оптимизатору запросов не приходится прибегать к догадкам селективности).

Выполняя приведенный ниже запрос к базе данных кредитов, я получаю разные оценки под разными CE. По новому CE я получаю оценку в 900 строк, которую я ожидал, по старому CE я получаю оценку 241,416, и я не могу понять, как эта оценка получена. Кто-нибудь может пролить свет?

-- New CE (Estimate = 900)
DECLARE @LastName VARCHAR(15) = 'BA%'
SELECT * FROM [Credit].[dbo].[member]
WHERE [lastname] LIKE @LastName;

-- Forcing Legacy CE (Estimate = 241.416)
DECLARE @LastName VARCHAR(15) = 'BA%'
SELECT * FROM [Credit].[dbo].[member]
WHERE [lastname] LIKE @LastName
OPTION (
QUERYTRACEON 9481,
QUERYTRACEON 9292,
QUERYTRACEON 9204,
QUERYTRACEON 3604
);

В моем сценарии у меня уже есть база данных кредитов, установленная на уровень совместимости 120, поэтому во втором запросе я использую флаги трассировки для принудительного использования устаревшего CE, а также для предоставления информации о том, какие статистические данные используются / учитываются оптимизатором запросов. Я могу видеть, как используется статистика по столбцу «фамилия», но я все еще не могу понять, как получается оценка 241.416.

Я не смог найти в Интернете ничего, кроме этой статьи Ицик Бен-Гана , в которой говорится: «При использовании предиката LIKE во всех случаях оптимизации для неизвестных сценариев как в старых, так и в новых CE используются оценки 9%». Информация в этом посте может показаться неверной.

FZA
источник

Ответы:

28

Предположение для LIKE вашего случая основано на:

  • G: Стандартное предположение 9% ( sqllang!x_Selectivity_Like)
  • M: Коэффициент 6 (магическое число)
  • D: Средняя длина данных в байтах (из статистики), округленная до целого числа

В частности, sqllang!CCardUtilSQL7::ProbLikeGuessиспользует:

Selectivity (S) = G / M * LOG(D)

Заметки:

  • LOG(D)Термин опускается , если Dнаходится между 1 и 2.
  • Если Dменьше 1 (в том числе для пропавших без вести или NULLстатистики):
    D = FLOOR(0.5 * maximum column byte length)

Этот вид причудливости и сложности довольно типичен для оригинального CE.

В примере с вопросом средняя длина равна 5 (5,6154 с DBCC SHOW_STATISTICSокруглением в меньшую сторону):

Эстимейт = 10 000 * (0,09 / 6 * LOG (5)) = 241,416

Другие примеры значений:

 D   = оценка с использованием формулы для S
 15 = 406,208
 14 = 395,859
 13 = 384,742
 12 = 372,736
 11 = 359,684
 10 = 345,388
 09 = 329,584
 08 = 311,916
 07 = 291,887
 06 = 268,764
 05 = 241,416
 04 = 207,944
 03 = 164,792
 02 = 150.000 (журнал не используется)
 01 = 150.000 (журнал не используется)
 00 = 291,887 (LOG 7) / * FLOOR (0,5 * 15) [15, поскольку фамилия varchar (15)] * /

Испытательный стенд

DECLARE
    @CharLength integer = 5, -- Set length here
    @Counter integer = 1;

CREATE TABLE #T (c1 varchar(15) NULL);

-- Add 10,000 rows
SET NOCOUNT ON;
SET STATISTICS XML OFF;

BEGIN TRANSACTION;
WHILE @Counter <= 10000
BEGIN
    INSERT #T (c1) VALUES (REPLICATE('X', @CharLength));
    SET @Counter = @Counter + 1;
END;
COMMIT TRANSACTION;

SET NOCOUNT OFF;
SET STATISTICS XML ON;

-- Test query
DECLARE @Like varchar(15);
SELECT * FROM #T AS T 
WHERE T.c1 LIKE @Like;

DROP TABLE #T;
Пол Уайт говорит, что GoFundMonica
источник
15

Я тестировал на SQL Server 2014 с устаревшим CE и не получил 9% в качестве оценки мощности. Я не смог найти ничего точного в Интернете, поэтому я провел некоторое тестирование и нашел модель, которая подходит для всех тестовых случаев, которые я пробовал, но я не уверен, что она завершена.

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

Если FLOOR (средняя длина ключа) = 0, тогда формула оценки игнорирует статистику столбца и создает оценку на основе длины типа данных. Я тестировал только с VARCHAR (N), поэтому возможно, что есть другая формула для NVARCHAR (N). Вот формула для VARCHAR (N):

(оценка строки) = (строки в таблице) * (-0,004869 + 0,032649 * log10 (длина типа данных))

Это очень хорошо подходит, но не совсем точно:

граф первой формулы

Ось X - это длина типа данных, а ось Y - это количество оценочных строк для таблицы с 1 миллионом строк.

Оптимизатор запросов будет использовать эту формулу, если у вас нет статистики по столбцу или если в столбце достаточно значений NULL, чтобы средняя длина ключа была ниже 1.

Например, предположим, что у вас была таблица с 150 тыс. Строк с фильтрацией по VARCHAR (50) и без статистики по столбцам. Прогноз оценки строки:

150000 * (-0,004869 + 0,032649 * log10 (50)) = 7590,1 строки

SQL, чтобы проверить это:

CREATE TABLE X_CE_LIKE_TEST_1 (
STRING VARCHAR(50)
);

CREATE STATISTICS X_STAT_CE_LIKE_TEST_1 ON X_CE_LIKE_TEST_1 (STRING) WITH NORECOMPUTE;

WITH
    L0 AS (SELECT 1 AS c UNION ALL SELECT 1),
    L1 AS (SELECT 1 AS c FROM L0 A CROSS JOIN L0 B),
    L2 AS (SELECT 1 AS c FROM L1 A CROSS JOIN L1 B),
    L3 AS (SELECT 1 AS c FROM L2 A CROSS JOIN L2 B),
    L4 AS (SELECT 1 AS c FROM L3 A CROSS JOIN L3 B CROSS JOIN L2 C),
    NUMS AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS NUM FROM L4)  
    INSERT INTO X_CE_LIKE_TEST_1 WITH (TABLOCK) (STRING)
    SELECT TOP (150000) 'ZZZZZ'
    FROM NUMS
    ORDER BY NUM;

DECLARE @LastName VARCHAR(15) = 'BA%'
SELECT * FROM X_CE_LIKE_TEST_1
WHERE STRING LIKE @LastName;

SQL Server дает приблизительное количество строк в 7242,47, что довольно близко.

Если FLOOR (средняя длина ключа)> = 1, то используется другая формула, основанная на значении FLOOR (средняя длина ключа). Вот таблица некоторых значений, которые я пробовал:

1    1.5%
2    1.5%
3    1.64792%
4    2.07944%
5    2.41416%
6    2.68744%
7    2.91887%
8    3.11916%
9    3.29584%
10   3.45388%

Если FLOOR (средняя длина ключа) <6, используйте таблицу выше. В противном случае используйте следующее уравнение:

(оценка строки) = (строки в таблице) * (-0,003381 + 0,034539 * log10 (FLOOR (средняя длина ключа)))

Этот лучше подходит, чем другой, но все еще не совсем точен.

график второй формулы

Ось X - это средняя длина ключа, а ось Y - количество оценочных строк для таблицы с 1 миллионом строк.

Чтобы привести другой пример, предположим, что у вас была таблица с 10 тыс. Строк со средней длиной ключа 5,5 для статистики по отфильтрованному столбцу. Оценка строки будет:

10000 * 0,241416 = 241,416 строк.

SQL, чтобы проверить это:

CREATE TABLE X_CE_LIKE_TEST_2 (
STRING VARCHAR(50)
);

WITH
    L0 AS (SELECT 1 AS c UNION ALL SELECT 1),
    L1 AS (SELECT 1 AS c FROM L0 A CROSS JOIN L0 B),
    L2 AS (SELECT 1 AS c FROM L1 A CROSS JOIN L1 B),
    L3 AS (SELECT 1 AS c FROM L2 A CROSS JOIN L2 B),
    L4 AS (SELECT 1 AS c FROM L3 A CROSS JOIN L3 B CROSS JOIN L2 C),
    NUMS AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS NUM FROM L4)  
    INSERT INTO X_CE_LIKE_TEST_2 WITH (TABLOCK) (STRING)
    SELECT TOP (10000) 
    CASE 
      WHEN NUM % 2 = 1 THEN REPLICATE('Z', 5) 
      ELSE REPLICATE('Z', 6)
    END
    FROM NUMS
    ORDER BY NUM;

CREATE STATISTICS X_STAT_CE_LIKE_TEST_2 ON X_CE_LIKE_TEST_2 (STRING) 
WITH NORECOMPUTE, FULLSCAN;

DECLARE @LastName VARCHAR(15) = 'BA%'
SELECT * FROM X_CE_LIKE_TEST_2
WHERE STRING LIKE @LastName;

Оценка строки 241,416 соответствует тому, что у вас есть в вопросе. Там было бы какая-то ошибка, если бы я использовал значение не в таблице.

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

Джо Оббиш
источник