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


24

У мене склалося враження, що при використанні LIKEоператора в усіх оптимізаціях для невідомих сценаріїв, як застарілі, так і нові СЕ використовують оцінку 9% (за умови, що відповідна статистика доступна і оптимізатору запитів не потрібно вдаватися до припущень про вибірковість).

Під час виконання нижчезазначеного запиту проти кредитної бази даних я отримую різні оцінки за різними СЕ. Відповідно до нового СЕ, я отримую оцінку в 900 рядків, яку я очікував, за спадщиною СЕ я отримую оцінку в 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, отже чому у другому запиті я використовую прапори слідів для примусу застарілого СЕ та для надання інформації про те, які статистичні дані використовуються / вважаються оптимізатором запитів. Я бачу, як використовуються статистичні дані стовпців про "прізвище", але я все ще не можу розробити, як отримується оцінка 241.416.

В Інтернеті я не міг знайти нічого іншого, окрім цієї статті Іцік Бен-Гана , де сказано: "При використанні предиката LIKE у всіх оптимізованих для невідомих сценаріях як застарілі, так і нові КЕ використовують 9-відсоткову оцінку". Інформація в цій публікації видається невірною.

Відповіді:


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)

Цей вид химерності та складності досить характерний для оригінального СЕ.

У прикладі запитання середня довжина дорівнює 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 (LOG не використовується)
 01 = 150 000 (LOG не використовується)
 00 = 291,887 (ЛОГ 7) / * ПОЛЯ (0,5 * 15) [15, оскільки прізвище варчар (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;

15

Я тестував на SQL Server 2014 зі спадщиною CE і не отримав 9% як оцінка кардинальності. Я не зміг знайти нічого точного в Інтернеті, тому я провів тестування, і знайшов модель, яка відповідає всім тестам, які я намагався, але я не можу бути впевнений, що це закінчено.

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

Якщо FLOOR (середня довжина ключа) = 0, то формула оцінки ігнорує статистику стовпців і створює оцінку на основі довжини типу даних. Я протестував лише з VARCHAR (N), тому можливо, що для NVARCHAR (N) існує інша формула. Ось формула для VARCHAR (N):

(оцінка рядка) = (рядки в таблиці) * (-0.004869 + 0.032649 * log10 (довжина типу даних))

Це дуже добре підходить, але це не зовсім точно:

Перший графік формули

Вісь x - це довжина типу даних, а вісь y - кількість оцінених рядків для таблиці з 1 мільйоном рядків.

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

Наприклад, припустимо, що у вас була таблиця з 150k рядками з фільтруванням по 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 (ПОЛІ (середня довжина ключа)))

Цей має кращу форму, ніж інший, але все ще не зовсім точний.

Графік другої формули

Вісь x - середня довжина ключа, а вісь y - кількість оцінених рядків для таблиці з 1 мільйоном рядків.

Для приклад іншого прикладу, припустимо, що у вас була таблиця з 10k рядками із середньою довжиною ключа 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, що відповідає тому, що ви маєте в питанні. Була б якась помилка, якби я використовував значення, яке не в таблиці.

Моделі тут не є ідеальними, але я думаю, що вони досить добре ілюструють загальну поведінку.

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