Kardinalitätsschätzung für LIKE-Operator (lokale Variablen)

24

Ich hatte den Eindruck, dass bei Verwendung des LIKEOperators in allen für unbekannte Szenarien optimierten Szenarien sowohl die alten als auch die neuen CEs eine Schätzung von 9% verwenden (vorausgesetzt, dass relevante Statistiken verfügbar sind und der Abfrageoptimierer nicht auf Selektivitätsschätzungen zurückgreifen muss).

Wenn ich die folgende Abfrage für die Kreditdatenbank durchführe, erhalte ich unterschiedliche Schätzungen unter den verschiedenen CEs. Unter dem neuen CE erhalte ich eine Schätzung von 900 Zeilen, die ich erwartet hatte, unter dem älteren CE erhalte ich eine Schätzung von 241,416 und ich kann nicht herausfinden, wie diese Schätzung abgeleitet wird. Kann jemand Licht ins Dunkel bringen?

-- 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
);

In meinem Szenario ist die Kreditdatenbank bereits auf Kompatibilitätsstufe 120 eingestellt. Daher verwende ich in der zweiten Abfrage Ablaufverfolgungsflags, um das ältere CE zu erzwingen und Informationen darüber bereitzustellen, welche Statistiken vom Abfrageoptimierer verwendet / berücksichtigt werden. Ich kann sehen, dass die Spaltenstatistiken zu 'Nachname' verwendet werden, kann aber immer noch nicht herausfinden, wie die Schätzung von 241,416 abgeleitet wird.

Ich konnte nichts anderes online finden als diesen Itzik Ben-Gan-Artikel , in dem es heißt: "Wenn Sie das LIKE-Prädikat in allen Optimierungsszenarien für unbekannte Szenarien verwenden, verwenden sowohl ältere als auch neue CEs eine Schätzung von 9 Prozent." Die Informationen in diesem Beitrag scheinen falsch zu sein.

Fza
quelle

Antworten:

28

Die Vermutung für LIKE in Ihrem Fall basiert auf:

  • G: Die Standard 9% Vermutung ( sqllang!x_Selectivity_Like)
  • M: Faktor 6 (magische Zahl)
  • D: Durchschnittliche Datenlänge in Bytes (aus Statistiken), auf ganze Zahlen abgerundet

Insbesondere sqllang!CCardUtilSQL7::ProbLikeGuessverwendet:

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

Anmerkungen:

  • Der LOG(D)Begriff wird weggelassen, wenn er Dzwischen 1 und 2 liegt.
  • Wenn Dist weniger als 1 (auch für fehlende oder NULLStatistik):
    D = FLOOR(0.5 * maximum column byte length)

Diese Art von Eigenartigkeit und Komplexität ist typisch für das ursprüngliche CE.

Im Beispiel der Frage beträgt die durchschnittliche Länge 5 (5.6154 von DBCC SHOW_STATISTICSabgerundet):

Schätzung = 10.000 * (0,09 / 6 * LOG (5)) = 241,416

Andere Beispielwerte:

 D   = Schätzen mit der Formel für 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 nicht verwendet)
 01 = 150.000 (LOG nicht verwendet)
 00 = 291.887 (LOG 7) / * FLOOR (0.5 * 15) [15 da der Nachname varchar (15) ist] * /

Prüfstand

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;
Paul White sagt GoFundMonica
quelle
15

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:

erste Formelgraphik

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.

zweites Formeldiagramm

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.

Joe Obbish
quelle