Wie ermittelt SQL Server die Schlüsselspaltenreihenfolge in fehlenden Indexanforderungen?

32

Wie ermittelt SQL Server die Reihenfolge der Schlüsselspalten in den fehlenden Indexempfehlungen für einen Abfrageplan?

Bryan Rebok
quelle

Antworten:

44

Wenn SQL Server eine fehlende Indexempfehlung für einen bestimmten Abfrageplan erstellt, werden mögliche Schlüsselspalten in zwei Gruppen unterteilt. Der erste Satz enthält alle empfohlenen Spalten, die Teil eines EQUALITY-Prädikats sind. Die zweite Gruppe enthält alle empfohlenen Spalten, die Teil eines Ungleichheitsprädikats sind.

Innerhalb jeder Gruppe werden die Spalten nach der Ordnungsposition der Spalten sortiert, basierend auf der Tabellendefinition.

(Vielen Dank an Brent Ozar, der ein Reproskript gegen die Stack Overflow-Datenbank erstellt hat, um dies zu beweisen!)

1. Erstellen Sie drei identische Tabellen , aber ordnen Sie ihre Spalten in unterschiedlicher Reihenfolge an. (Der Grund hierfür ist, dass verschiedene Spaltennamen und Datentypen verwendet werden, um zu zeigen, dass dies die Spaltenreihenfolge in der fehlenden Indexempfehlung nicht beeinflusst.)

CREATE TABLE dbo.NumberLetterDate (ID INT IDENTITY(1,1) PRIMARY KEY CLUSTERED, 
fINT INT, fNVARCHAR NVARCHAR(40), fDATE DATETIME, AboutMe NVARCHAR(MAX));
GO
CREATE TABLE dbo.LetterDateNumber (ID INT IDENTITY(1,1) PRIMARY KEY CLUSTERED, 
fNVARCHAR NVARCHAR(40), fDATE DATETIME, fINT INT, AboutMe NVARCHAR(MAX));
GO
CREATE TABLE dbo.DateNumberLetter (ID INT IDENTITY(1,1) PRIMARY KEY CLUSTERED,
fDATE DATETIME, fINT INT, fNVARCHAR NVARCHAR(40), AboutMe NVARCHAR(MAX));
GO

2. Füllen Sie die Tabellen mit denselben Daten. Holen Sie sich 100.000 Zeilen aus der Users-Tabelle mit realer Datenverteilung.

INSERT INTO dbo.NumberLetterDate(fINT, fNVARCHAR, fDATE, AboutMe)
SELECT TOP 100000 Age, DisplayName, LastAccessDate, AboutMe
  FROM dbo.Users WITH (NOLOCK)
  ORDER BY Id;
GO
INSERT INTO dbo.LetterDateNumber(fINT, fNVARCHAR, fDATE, AboutMe)
SELECT TOP 100000 Age, DisplayName, LastAccessDate, AboutMe
  FROM dbo.Users WITH (NOLOCK)
  ORDER BY Id;
GO
INSERT INTO dbo.DateNumberLetter(fINT, fNVARCHAR, fDATE, AboutMe)
SELECT TOP 100000 Age, DisplayName, LastAccessDate, AboutMe
  FROM dbo.Users WITH (NOLOCK)
  ORDER BY Id;
GO

3. Schreiben Sie eine Abfrage, die einen Index benötigt. Beginnen Sie mit 3 Gleichheitsfiltern, die in allen 3 Feldern nach einem genauen Wert filtern. Beachten Sie, dass alle 3 Abfragen dieselben Felder in derselben Reihenfolge haben:

SELECT ID
  FROM dbo.NumberLetterDate
  WHERE fINT = 100
  AND fNVARCHAR = 'Brent Ozar'
  AND fDATE = '2018/01/01'
  AND 1 = (SELECT 1);

SELECT ID
  FROM dbo.LetterDateNumber
  WHERE fINT = 100
  AND fNVARCHAR = 'Brent Ozar'
  AND fDATE = '2018/01/01'
  AND 1 = (SELECT 1);

SELECT ID
  FROM dbo.DateNumberLetter
  WHERE fINT = 100
  AND fNVARCHAR = 'Brent Ozar'
  AND fDATE = '2018/01/01'
  AND 1 = (SELECT 1);
GO

Alle drei Tabellen haben genau die gleichen Daten und die Abfragen sind identisch. Der einzige Unterschied ist die Reihenfolge der Felder - und das ist auch der Unterschied bei unseren fehlenden Indexanforderungen:

Ausführungspläne mit 3 Gleichheitsfeldern

In den Ausführungsplänen stimmt die Spaltenreihenfolge in der fehlenden Indexanforderung genau mit der Spaltenreihenfolge in der Tabelle überein. In dbo.NumberLetterDate steht beispielsweise die Zahlenspalte an erster Stelle, sodass sie auch in der fehlenden Indexanforderung an erster Stelle steht:

  • Am dbo.NumberLetterDate befindet sich der fehlende Index auf fINT (Zahl), fLetter (Nvarchar), fDate, die gleiche Reihenfolge der Felder in der Tabelle
  • Bei dbo.LetterDateNumber wechselt die Indexreihenfolge zu fNVARCHAR, fDATE, fINT
  • Am dbo.DateNumberLetter wechselt die Indexreihenfolge zu fDATE, fINT, fNVARCHAR

Bei einer Einzeltabellenoperation wie dieser scheint die Reihenfolge der Indexfelder nicht von der Selektivität, dem Datentyp oder der Position in der Abfrage abzuhängen. (Ich überlasse es anderen Leuten, dies mit komplexeren Abfragen und Verknüpfungen zu beweisen.)

4. Mischen Sie einen Ungleichheitsfilter ein. Geben Sie im Feld INT beispielsweise <> 100 als Filter ein:

SELECT ID
  FROM dbo.NumberLetterDate
  WHERE fINT <> 100
  AND fNVARCHAR = 'Brent Ozar'
  AND fDATE = '2018/01/01'
  AND 1 = (SELECT 1);

SELECT ID
  FROM dbo.LetterDateNumber
  WHERE fINT <> 100
  AND fNVARCHAR = 'Brent Ozar'
  AND fDATE = '2018/01/01'
  AND 1 = (SELECT 1);

SELECT ID
  FROM dbo.DateNumberLetter
  WHERE fINT <> 100
  AND fNVARCHAR = 'Brent Ozar'
  AND fDATE = '2018/01/01'
  AND 1 = (SELECT 1);
GO

In den Ausführungsplänen werden zuerst Gleichheitsfelder und dann Ungleichheitsfelder angezeigt. In diesem Fall wird fINT in allen drei fehlenden Indexanforderungen als letztes angezeigt, da es sich um eine Ungleichheitssuche handelt:

Ausführungspläne mit 2 Gleichheits- und 1 Ungleichheitssuche

5. Verwenden Sie 3 Ungleichungsfilter. Verwenden Sie für alle Felder die gleiche Suche (<>):

SELECT ID
  FROM dbo.NumberLetterDate
  WHERE fINT <> 100
  AND fNVARCHAR <> 'Brent Ozar'
  AND fDATE <> '2018/01/01'
  AND 1 = (SELECT 1);

SELECT ID
  FROM dbo.LetterDateNumber
  WHERE fINT <> 100
  AND fNVARCHAR <> 'Brent Ozar'
  AND fDATE <> '2018/01/01'
  AND 1 = (SELECT 1);

SELECT ID
  FROM dbo.DateNumberLetter
  WHERE fINT <> 100
  AND fNVARCHAR <> 'Brent Ozar'
  AND fDATE <> '2018/01/01'
  AND 1 = (SELECT 1);
GO

Da es keine Gleichheitssuche gibt, haben alle drei Felder in der fehlenden Indexempfehlung die gleiche Prioritätsreihenfolge, und jetzt sortieren wir wieder nur nach der Feldreihenfolge:

Ausführungspläne mit 3 Ungleichheitssuchen

Bryan Rebok
quelle