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