Gibt es einen Grund, sowohl einen Index mit eingeschlossenen als auch einen Index ohne Spalten zu haben?

7

Ich sehe mir einige langsam laufende Abfragen in einer Datenbank an, die in den letzten Jahren (unter einer Reihe verschiedener Entwickler) erheblich weiterentwickelt wurde.
Eine der am häufigsten verwendeten Tabellen enthält einige Indizes mit denselben indizierten Spalten, eine enthält jedoch Spalten und die andere nicht.

Zum Beispiel:
Ein Index:

 CREATE NONCLUSTERED INDEX [IDX_tblTable_IndexedColumnId_Inc] ON [dbo].[tblTable]
 (
[IndexedColumnId] ASC
 )

Und dann der andere:

CREATE NONCLUSTERED INDEX [IDX_tblTable_IndexedColumnId_Inc] ON [dbo].[tblTable]
(
[IndexedColumnId] ASC
)
INCLUDE (   [Field1Id],
[Field2],
[Field3],
[Field4],
[Field5],
[Field6]) 

Ich würde annehmen, dass dies nur ein Versehen ist und nur Speicherplatz und zusätzlichen Overhead verbraucht, um den Index ohne die enthaltenen Spalten zu haben (die enthaltenen Spalten werden bekanntermaßen benötigt).

Gibt es einen Vorteil, wenn zwei Indizes in derselben Spalte vorhanden sind, einer mit eingeschlossenen Spalten und einer ohne?

Lithium
quelle

Antworten:

8

Um ein wenig näher auf das einzugehen, was das beliebteste n-dimensionale Analogon aller kommentiert hat, kann es von Vorteil sein, beides zu haben.

Hier ist eine kleine (aber nicht unzureichende) Demo.

Das Nötige:

USE StackOverflow;
SET NOCOUNT ON; 

CREATE TABLE dbo.IndexCrap
(
    Id INT IDENTITY PRIMARY KEY CLUSTERED,
    Whatever DATETIME,
    Nonsense VARCHAR(50),
    Etc UNIQUEIDENTIFIER,
    SoForth BIT
);

INSERT dbo.IndexCrap WITH (TABLOCK)
        (Whatever, Nonsense, Etc, SoForth )
SELECT TOP 1000 DATEADD(DAY, x.n, GETDATE()),
                REPLICATE('A', x.n % 50),
                NEWID(),
                CASE WHEN x.n % 15 = 0 THEN 1 ELSE 0 END
FROM (SELECT ROW_NUMBER() OVER (ORDER BY @@ROWCOUNT) AS n
        FROM sys.messages AS m ) AS x;

Die Indizes:

CREATE INDEX ix_tinydancer ON dbo.IndexCrap (Whatever);

CREATE INDEX ix_largemarge ON dbo.IndexCrap (Whatever) INCLUDE (Nonsense, Etc, SoForth);

So wie Ihre Frage zum Index für eine Spalte, ein Index für dieselbe Spalte mit einigen Includes.

Die Fragen:

SELECT COUNT(*)
FROM dbo.IndexCrap AS ic;

SELECT ic.Nonsense, ic.Etc, ic.SoForth
FROM dbo.IndexCrap AS ic
WHERE ic.Whatever >= DATEADD(DAY, 500, GETDATE());

Die Ausführungspläne:

Für die COUNT(*)Abfrage wählt der Optimierer unseren kleinsten Einzelspaltenindex gegenüber dem breiteren nicht gruppierten Index und dem etwas breiteren gruppierten Index aus, da er nicht gefiltert ist und wir lediglich eine Zeilenanzahl benötigen.

NÜSSE

Für die umfassendere Abfrage wählt der Optimierer den Abdeckungsindex aus, damit er nicht a) den Clustered-Index scannen oder b) auf den engeren Index zugreifen und Schlüsselsuchen zurück zum Clustered-Index durchführen muss.

NÜSSE

Natürlich kennen Sie möglicherweise nicht jede einzelne Abfrage, die auf Ihrem System ausgeführt wird. Deshalb schreibt das Unternehmen, für das ich arbeite sp_BlitzIndex, ein kostenloses Tool zur Analyse Ihrer Indizes .

Wenn wir laufen EXEC master.dbo.sp_BlitzIndex @DatabaseName = 'StackOverflow', @Mode = 4;, sind hier einige Ergebnisse, die uns interessieren:

NÜSSE

Die beiden von mir hinzugefügten Indizes IndexCrap(ohne PK / CX) werden mit doppelten Schlüsselspalten gekennzeichnet. Es gibt auch ihre Definitionen, Verwendung, Größe und vieles mehr, die nicht in der Bildschirmabdeckung angezeigt werden.

Die Nutzungszähler sind nicht perfekt. Sie werden zurückgesetzt, wenn Sie Indizes hinzufügen oder löschen. In einigen Versionen von SQL Server werden sie zurückgesetzt, wenn Sie Indizes neu erstellen, und natürlich werden sie zurückgesetzt, wenn Sie SQL Server neu starten.

Hoffe das hilft!

Erik Darling
quelle