Ich habe auf SQL Server 2014 mit dem Legacy-CE getestet und auch keine 9% als Kardinalitätsschätzung erhalten. Ich konnte online nichts Genaues finden, also habe ich einige Tests durchgeführt und ein Modell gefunden, das zu allen von mir getesteten Testfällen passt, aber ich kann nicht sicher sein, ob es vollständig ist.
In dem Modell, das ich gefunden habe, wird die Schätzung aus der Anzahl der Zeilen in der Tabelle, der durchschnittlichen Schlüssellänge der Statistik für die gefilterte Spalte und manchmal der Datentyplänge der gefilterten Spalte abgeleitet. Für die Schätzung werden zwei verschiedene Formeln verwendet.
Wenn FLOOR (durchschnittliche Schlüssellänge) = 0 ist, ignoriert die Schätzformel die Spaltenstatistik und erstellt eine Schätzung basierend auf der Datentyplänge. Ich habe nur mit VARCHAR (N) getestet, daher gibt es möglicherweise eine andere Formel für NVARCHAR (N). Hier ist die Formel für VARCHAR (N):
(Zeilenschätzung) = (Zeilen in Tabelle) * (-0.004869 + 0.032649 * log10 (Länge des Datentyps))
Dies hat eine sehr gute Passform, ist aber nicht genau:
Die x-Achse ist die Länge des Datentyps und die y-Achse ist die Anzahl der geschätzten Zeilen für eine Tabelle mit 1 Million Zeilen.
Das Abfrageoptimierungsprogramm würde diese Formel verwenden, wenn Sie keine Statistik für die Spalte haben oder wenn die Spalte über genügend NULL-Werte verfügt, um die durchschnittliche Schlüssellänge auf unter 1 zu senken.
Angenommen, Sie hätten eine Tabelle mit 150.000 Zeilen mit einer Filterung in einem VARCHAR (50) und ohne Spaltenstatistik. Die Zeilenschätzungsvorhersage lautet:
150000 * (-0,004869 + 0,032649 * log10 (50)) = 7590,1 Zeilen
SQL zum Testen:
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 gibt eine geschätzte Zeilenzahl von 7242,47 an, was eine Art Abschluss darstellt.
Wenn FLOOR (durchschnittliche Schlüssellänge)> = 1 ist, wird eine andere Formel verwendet, die auf dem Wert von FLOOR (durchschnittliche Schlüssellänge) basiert. Hier ist eine Tabelle mit einigen Werten, die ich ausprobiert habe:
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%
Wenn FLOOR (durchschnittliche Schlüssellänge) <6, verwenden Sie die obige Tabelle. Ansonsten verwende die folgende Gleichung:
(Zeilenschätzung) = (Zeilen in Tabelle) * (-0,003381 + 0,034539 * log10 (FLOOR (durchschnittliche Schlüssellänge))
Dieser hat eine bessere Passform als der andere, ist aber immer noch nicht genau genug.
Die x-Achse ist die durchschnittliche Schlüssellänge und die y-Achse die Anzahl der geschätzten Zeilen für eine Tabelle mit 1 Million Zeilen.
Nehmen wir an, Sie hätten eine Tabelle mit 10.000 Zeilen mit einer durchschnittlichen Schlüssellänge von 5,5 für die Statistik der gefilterten Spalte. Die Zeilenschätzung wäre:
10000 * 0,241416 = 241,416 Zeilen.
SQL zum Testen:
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;
Die Zeilenschätzung ist 241.416, was mit Ihrer Frage übereinstimmt. Es würde ein Fehler auftreten, wenn ich einen Wert verwende, der nicht in der Tabelle enthalten ist.
Die Modelle hier sind nicht perfekt, aber ich denke, dass sie das allgemeine Verhalten ziemlich gut veranschaulichen.