Änderungen an Schätzungen für Prädikate, die SUBSTRING () in SQL Server 2016 enthalten?


13

Gibt es eine Dokumentation oder Recherche zu Änderungen in SQL Server 2016 hinsichtlich der Schätzung der Kardinalität für Prädikate, die SUBSTRING () oder andere Zeichenfolgenfunktionen enthalten?

Der Grund, den ich frage, ist, dass ich eine Abfrage betrachte, deren Leistung sich im Kompatibilitätsmodus 130 verschlechtert hat, und der Grund eine Änderung in der Schätzung der Anzahl der Zeilen war, die einer WHERE-Klausel entsprechen, die einen Aufruf von SUBSTRING () enthielt. Ich habe das Problem mit einem Umschreiben der Abfrage behoben, mich jedoch gefragt, ob jemand Kenntnis von der Dokumentation zu Änderungen in diesem Bereich in SQL Server 2016 hat.

Demo-Code ist unten. Die Schätzungen liegen in diesem Testfall sehr nahe beieinander, die Genauigkeit variiert jedoch abhängig von den Daten.

Im Testfall verwendet SQL Server auf Kompatibilitätsstufe 120 anscheinend das Histogramm für die Schätzung, während SQL Server auf Kompatibilitätsstufe 130 anscheinend festgelegte 10% der Tabellenübereinstimmungen annimmt.

CREATE DATABASE MyStringTestDB;
GO
USE MyStringTestDB;
GO
DROP TABLE IF EXISTS dbo.StringTest;
CREATE TABLE dbo.StringTest ( [TheString] varchar(15) );
GO
INSERT INTO dbo.StringTest
VALUES
( 'Y5_CLV' );
INSERT INTO dbo.StringTest
VALUES
( 'Y5_EG3' );
INSERT INTO dbo.StringTest
VALUES
( 'ZY_NE' );
INSERT INTO dbo.StringTest
VALUES
( 'ZY_PQT' );
INSERT INTO dbo.StringTest
VALUES
( 'ZY_T2V' );
INSERT INTO dbo.StringTest
VALUES
( 'ZY_TT4' );
INSERT INTO dbo.StringTest
VALUES
( 'ZY_ZKK' );
INSERT INTO dbo.StringTest
VALUES
( 'ZZ_LW6' );
INSERT INTO dbo.StringTest
VALUES
( 'ZZ_QO3' );
INSERT INTO dbo.StringTest
VALUES
( 'ZZ_TZ7' );
INSERT INTO dbo.StringTest
VALUES
( 'ZZ_UZZ' );

CREATE CLUSTERED INDEX IX_Clustered ON dbo.StringTest (TheString);

/* 
Uses fixed % for estimate; 1.1 rows estimated in this case.
    Plan for computation:
        CSelCalcFixedFilter (0.1) <----
            Selectivity: 0.1
*/
ALTER DATABASE MyStringTestDB SET compatibility_level = 130;
GO
SELECT * 
FROM dbo.StringTest 
WHERE SUBSTRING(TheString, 1, CHARINDEX('_',TheString) - 1) = 'ZZ'
OPTION (QUERYTRACEON 2363, QUERYTRACEON 3604);

/* 
Uses histogram to get estimate of 1
 CSelCalcPointPredsFreqBased <----
      Distinct value calculation:
          CDVCPlanLeaf
              0 Multi-Column Stats, 1 Single-Column Stats, 0 Guesses
      Individual selectivity calculations:
          (none)
    Loaded histogram for column QCOL: [DBA].[dbo].[StringTest].TheString from stats with id 1
*/
ALTER DATABASE MyStringTestDB SET compatibility_level = 120;
GO
SELECT * 
FROM dbo.StringTest 
WHERE SUBSTRING(TheString, 1, CHARINDEX('_',TheString) - 1) = 'ZZ'
OPTION (QUERYTRACEON 2363, QUERYTRACEON 3604);

/*
-- Simpler rewrite; works fine in both compat levels and gets better estimate.
SELECT * 
FROM dbo.StringTest 
WHERE TheString LIKE 'ZZ[_]%'
OPTION (QUERYTRACEON 2363, QUERYTRACEON 3604);
*/

1
Sie sind sich der Frage nicht sicher, aber wenn die Y5_EG3Zeichenfolgen nur Codes sind und immer in Großbuchstaben geschrieben werden, können Sie immer versuchen, eine binäre Sortierung anzugeben, Latin1_General_100_BIN2die die Geschwindigkeit der Filtervorgänge verbessern soll. Ergänzen Sie einfach COLLATE Latin1_General_100_BIN2die CREATE TABLEAussage, gleich nach dem varchar(15). Ich wäre gespannt, ob sich dies auch auf die Erstellung / Schätzung des Plans auswirkt.
Solomon Rutzky

Antworten:


8

Mir sind keine Unterlagen bekannt. Ich habe dies untersucht und einige Beobachtungen gemacht, die jedoch für einen Kommentar zu lang sind.

Die Schätzung von 10% ist nicht immer eine Verschlechterung. Nehmen Sie das folgende Beispiel.

TRUNCATE TABLE dbo.StringTest

INSERT INTO dbo.StringTest
SELECT TOP (1000000) 'ZZ_' + LEFT(NEWID(), 12)
FROM   master..spt_values v1,
       master..spt_values v2;

und die WHEREKlausel in Ihrer Frage.

WHERE SUBSTRING(TheString, 1, CHARINDEX('_',TheString) - 1) = 'ZZ'

Die Tabelle enthält eine Million Zeilen. Sie alle stimmen mit dem Prädikat überein. Unter der Kompatibilitätsstufe 130 ergibt die Schätzung von 10% eine Schätzung von 100.000. Unter 120 sind die geschätzten Zeilen 1.03913.

Das Verhalten 120 verwendet das Histogramm, um jedoch nur die Anzahl der verschiedenen Zeilen zu ermitteln. Der Dichtevektor in meinem Fall zeigt 1.039131E-06 und dieser wird mit der Tabellenkardinalität multipliziert, um die geschätzte Zeilenzahl zu erhalten. Alle Werte sind in der Tat unterschiedlich, aber alle stimmen mit dem Prädikat überein.

Die Verfolgung des query_optimizer_estimate_cardinalityerweiterten Ereignisses zeigt, dass es unter 130 zwei verschiedene <StatsCollection Name="CStCollFilter"Ereignisse gibt. Der erste schätzt 100.000. Der zweite lädt das Histogramm und verwendet den CSelCalcPointPredsFreqBased / DistinctCountCalculator, um die 1,04-Schätzung zu erhalten. Dieses zweite Ergebnis scheint nicht verwendet zu werden.

Das von Ihnen beobachtete Verhalten wird in 130 nicht konsequent angewendet. Ich habe hinzugefügt, ORDER BY TheStringdass dies ein klarer Gewinn für den 130-Schätzer sein wird, da die 120 mit einer Speichergewährung für eine Zeile weiterkämpfen, aber diese geringfügige Änderung war ausreichend, um die geschätzten Zeilen auf zu senken 1,03913 auch im 130er Fall.

Durch OPTION (QUERYRULEOFF SelectToFilter)das Hinzufügen wird die Schätzung für die Sortierung auf 100.000 zurückgesetzt, die Speicherzuweisung wird jedoch nicht erhöht, und die Schätzungen für die Sortierung basieren weiterhin auf den in der Tabelle definierten Werten.

Bildbeschreibung hier eingeben

In ähnlicher Weise war es im Fall von 130 ausreichend, den Kostenschwellenwert für die Parallelität zu optimieren, damit die Abfrage einen parallelen Plan erhält, um zur unteren Schätzung zurückzukehren. Das Hinzufügen QUERYTRACEON 8757bewirkt auch die niedrigere Schätzung. Es sieht so aus, als ob die Schätzung von 10% nur für unbedeutende Pläne beibehalten wird.

Deine vorgeschlagene Neufassung mit

WHERE TheString LIKE 'ZZ[_]%'

Zeigt viel bessere Schätzungen als beide. Die Ausgabe dafür ist

  CSelCalcTrieBased

      Column: QCOL: [MyStringTestDB].[dbo].[StringTest].TheString

Zeigen, dass es versucht hat . Mehr Informationen zu diesem im Abschnitt Zusammenfassung Statistiken Zeichenfolge nur oben hier .

Es ist jedoch nicht dasselbe wie Ihre ursprüngliche Abfrage. Da _nun angenommen wird, dass die erste Instanz von immer das dritte Zeichen ist, anstatt dynamisch gefunden zu werden.

Wenn diese Annahme in Ihrer ursprünglichen Abfrage festgeschrieben ist

 WHERE SUBSTRING(TheString, 1, 3) = 'ZZ_'

Die Schätzmethode ändert sich zu CSelCalcHistogramComparison(INTERVAL)und die geschätzten Zeilen werden genau.

Es ist in der Lage, dies in einen Bereich umzuwandeln

WHERE TheString >=  'ZZ_' AND TheString < ???

Verwenden Sie das Histogramm, um die Anzahl der Zeilen mit Werten in diesem Bereich zu schätzen.

Dies gilt jedoch nur für die Kardinalitätsschätzung. LIKEDies ist vorzuziehen, da eine Bereichssuche zur Laufzeit verwendet werden kann. SUBSTRING(TheString, 1, 3)oder LEFT(TheString, 3)kann nicht.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.