Perché usare la clausola INCLUDE quando si crea un indice?


432

Mentre studiavo per l'esame 70-433, ho notato che puoi creare un indice di copertura in uno dei due modi seguenti.

CREATE INDEX idx1 ON MyTable (Col1, Col2, Col3)

-- O --

CREATE INDEX idx1 ON MyTable (Col1) INCLUDE (Col2, Col3)

La clausola INCLUDE è nuova per me. Perché lo useresti e quali linee guida suggeriresti per determinare se creare un indice di copertura con o senza la clausola INCLUDE?

Risposte:


364

Se la colonna non è nella WHERE/JOIN/GROUP BY/ORDER BY, ma solo nell'elenco delle colonne nella SELECTclausola.

La INCLUDEclausola aggiunge i dati al livello più basso / foglia, anziché nella struttura dell'indice. Questo rende l'indice più piccolo perché non fa parte dell'albero

INCLUDE columnsnon sono colonne chiave nell'indice, quindi non sono ordinate. Ciò significa che non è davvero utile per predicati, ordinamento ecc., Come ho già detto. Tuttavia, può essere utile se hai una ricerca residua in poche righe dalle colonne chiave

Un altro articolo MSDN con un esempio funzionante


7
Quindi, questa sarebbe una tecnica per creare una versione meno costosa di un indice coperto?
JMarsch,

3
@gbn, ti dispiacerebbe spiegare questa frase in modo più dettagliato e spiegare perché ciò significa che la clausola include non è utile per l'ordinamento, ecc: "La clausola INCLUDE aggiunge i dati al livello più basso / foglia, piuttosto che nella struttura dell'indice . Questo rende l'indice più piccolo perché non fa parte dell'albero "
Tola Odejayi

4
@JMarsch: scusa per la risposta tardiva, ma sì, questo è esattamente quello che è.
gbn

10
@Tola Odejayi: le colonne INCLUDE non sono colonne chiave nell'indice, quindi non sono ordinate. Ciò non li rende in genere utili per JOIN o ordinamento. E poiché non sono colonne chiave, non siedono nell'intera struttura B-tree come colonne chiave
gbn

4
Mentre questa è la risposta più accettata, penso che siano necessarie ulteriori spiegazioni, cosa succede se per alcune domande la colonna è parte del SELECTe per alcuni no? \
Chisko

215

Utilizzare INCLUDE per aggiungere una o più colonne al livello foglia di un indice non cluster, se in tal modo è possibile "coprire" le query.

Immagina di dover cercare l'ID di un dipendente, l'ID reparto e il cognome.

SELECT EmployeeID, DepartmentID, LastName
FROM Employee
WHERE DepartmentID = 5

Se ti capita di avere un indice non cluster su (EmployeeID, DepartmentID), una volta che hai trovato i dipendenti per un determinato reparto, ora devi fare una "ricerca dei segnalibri" per ottenere il record completo effettivo dei dipendenti, solo per ottenere la colonna del cognome . Questo può diventare piuttosto costoso in termini di prestazioni, se trovi molti dipendenti.

Se avessi incluso quel cognome nel tuo indice:

CREATE NONCLUSTERED INDEX NC_EmpDep 
  ON Employee(EmployeeID, DepartmentID)
  INCLUDE (Lastname)

quindi tutte le informazioni necessarie sono disponibili a livello foglia dell'indice non cluster. Semplicemente cercando nell'indice non cluster e trovando i tuoi dipendenti per un determinato reparto, hai tutte le informazioni necessarie e la ricerca dei segnalibri per ogni dipendente trovato nell'indice non è più necessaria -> risparmi molto tempo.

Ovviamente, non puoi includere ogni colonna in ogni indice non cluster - ma se hai query che mancano solo una o due colonne per essere "coperte" (e che vengono usate molto), può essere molto utile INCLUDERE quelle in un indice non cluster adatto.


25
Sei sicuro di usare questo indice? Perché EmployeeID? Hai solo bisogno di DepartmentID nelle colonne chiave? Lei è stato citato qui come authoratitive: stackoverflow.com/q/6187904/27535
gbn

3
La tua spiegazione è buona ma in realtà non si allinea con il caso d'uso che descrivi. Le colonne chiave devono essere sul filtro o sulle JOINchiavi nella query e INCLUDEdevono essere i dati che stai recuperando ma non l'ordinamento.
JNK,

15
Innanzitutto l'indice Employee (EmployeeID, DepartmentID) non verrà utilizzato per filtrare DepartmentID = 5. Perché il suo ordine non corrisponde
AnandPhadke

29

Questa discussione non tiene conto del punto importante: la domanda non è se le "colonne non chiave" siano meglio includere come colonne- indice o come colonne- incluse .

La domanda è: quanto costa usare il meccanismo include per includere colonne che non sono realmente necessarie nell'indice ? (in genere non fa parte delle clausole where, ma è spesso incluso in select). Quindi il tuo dilemma è sempre:

  1. Usa indice su id1, id2 ... idN da solo o
  2. Usa indice su id1, id2 ... idN plus include col1, col2 ... colN

Dove: id1, id2 ... idN sono colonne spesso utilizzate nelle restrizioni e col1, col2 ... colN sono colonne spesso selezionate, ma in genere non utilizzate nelle restrizioni

(L'opzione di includere tutte queste colonne come parte della chiave di indice è sempre sempre stupida (a meno che non vengano utilizzate anche in restrizioni) - perché sarebbe sempre più costoso da mantenere poiché l'indice deve essere aggiornato e ordinato anche quando il le "chiavi" non sono cambiate).

Quindi utilizzare l'opzione 1 o 2?

Risposta: Se la tua tabella viene raramente aggiornata - per lo più inserita / eliminata da - allora è relativamente economico usare il meccanismo di inclusione per includere alcune "colonne attive" (che sono spesso usate in selezioni - ma spesso non utilizzate sulle restrizioni) poiché gli inserimenti / eliminazioni richiedono comunque che l'indice venga aggiornato / ordinato e quindi un piccolo overhead aggiuntivo è associato alla memorizzazione di alcune colonne extra mentre già si aggiorna l'indice. L'overhead è la memoria aggiuntiva e la CPU utilizzate per memorizzare informazioni ridondanti sull'indice.

Se le colonne che consideri di aggiungere come colonne incluse vengono spesso aggiornate (senza l'aggiornamento della chiave- indice -colonne) - oppure - se sono così tante che l'indice si avvicina a una copia della tabella - usa l'opzione 1 Suggerirei! Inoltre, se l'aggiunta di determinate colonne / inclusioni risulta che non fa alcuna differenza in termini di prestazioni, potresti voler saltare l'idea di aggiungerle :) Verifica che siano utili!

Anche il numero medio di righe per gli stessi valori nelle chiavi (id1, id2 ... idN) può avere una certa importanza.

Si noti che se una colonna, che viene aggiunta come una colonna di indice inclusa , viene utilizzata nella restrizione : Fintanto che l'indice in quanto tale può essere utilizzato (basato sulla restrizione contro l' indice- chiave -colonne), allora SQL Server corrisponde la restrizione della colonna rispetto all'indice (valori del nodo foglia) invece di andare nel modo costoso intorno alla tabella stessa.


18

Le colonne dell'indice di base vengono ordinate, ma le colonne incluse non vengono ordinate. Ciò consente di risparmiare risorse nel mantenimento dell'indice, consentendo al contempo di fornire i dati nelle colonne incluse per coprire una query. Pertanto, se si desidera coprire le query, è possibile inserire i criteri di ricerca per individuare le righe nelle colonne ordinate dell'indice, ma quindi "includere" colonne aggiuntive non ordinate con dati non di ricerca. Aiuta sicuramente a ridurre la quantità di ordinamento e frammentazione nella manutenzione dell'indice.


7

I motivi per cui (compresi i dati nel livello foglia dell'indice) sono stati ben spiegati. Il motivo per cui si danno due oscillazioni a questo proposito è che quando si esegue la query, se non si includono le colonne aggiuntive (nuova funzionalità in SQL 2005), SQL Server deve andare all'indice cluster per ottenere le colonne aggiuntive che richiede più tempo e aggiunge più carico al servizio SQL Server, ai dischi e alla memoria (cache del buffer per essere specifica) poiché le nuove pagine di dati vengono caricate in memoria, potenzialmente spingendo altri dati più spesso necessari fuori dalla cache del buffer.


c'è un modo per dimostrare che sta effettivamente usando meno memoria? è quello che mi aspetterei anch'io, ma sto diventando un po 'statico a riguardo al lavoro
Asken,

Dato che devi caricare in memoria la pagina dall'heap o dall'indice cluster, così come la pagina dell'indice, il che significa che stai mettendo in memoria dati duplicati, la matematica diventa piuttosto semplice. Per quanto riguarda un modo per misurarlo in modo specifico, no non c'è.
mrdenny,

5

Un'ulteriore considerazione che non ho visto nelle risposte già fornite è che le colonne incluse possono essere di tipi di dati non consentiti come colonne chiave di indice, come varchar (max).

Ciò consente di includere tali colonne in un indice di copertura. Di recente ho dovuto fare questo per fornire una query generata da nHibernate, che aveva molte colonne in SELECT, con un indice utile.


3

Un motivo per preferire INCLUDEle colonne chiave se non è necessaria quella colonna nella chiave è la documentazione. Ciò rende gli indici in evoluzione molto più facili in futuro.

Considerando il tuo esempio:

CREATE INDEX idx1 ON MyTable (Col1) INCLUDE (Col2, Col3)

Tale indice è il migliore se la tua query è simile alla seguente:

SELECT col2, col3
  FROM MyTable
 WHERE col1 = ...

Ovviamente non dovresti inserire colonne INCLUDEse puoi ottenere un ulteriore vantaggio dal fatto di averle nella parte chiave. Entrambe le seguenti query preferirebbero effettivamente la col2colonna nella chiave dell'indice.

SELECT col2, col3
  FROM MyTable
 WHERE col1 = ...
   AND col2 = ...
SELECT TOP 1 col2, col3
  FROM MyTable
 WHERE col1 = ...
 ORDER BY col2

Supponiamo che questo non sia il caso e lo abbiamo col2nella INCLUDEclausola perché non c'è proprio alcun vantaggio di averlo nella parte ad albero dell'indice.

Avanzamento veloce di alcuni anni.

È necessario ottimizzare questa query:

SELECT TOP 1 col2
  FROM MyTable
 WHERE col1 = ...
 ORDER BY another_col

Per ottimizzare quella query, il seguente indice sarebbe ottimo:

CREATE INDEX idx1 ON MyTable (Col1, another_col) INCLUDE (Col2)

Se controlli quali indici hai già su quella tabella, il tuo indice precedente potrebbe essere ancora lì:

CREATE INDEX idx1 ON MyTable (Col1) INCLUDE (Col2, Col3)

Ora lo sai Col2e Col3non fanno parte dell'albero dell'indice e non vengono quindi utilizzati per restringere l'intervallo dell'indice di lettura né per ordinare le righe. È piuttosto sicuro aggiungere another_columnalla fine della parte chiave dell'indice (dopo col1). C'è poco rischio di rompere qualcosa:

DROP INDEX idx1 ON MyTable;
CREATE INDEX idx1 ON MyTable (Col1, another_col) INCLUDE (Col2, Col3);

Tale indice diventerà più grande, il che comporta ancora alcuni rischi, ma è generalmente meglio estendere gli indici esistenti rispetto a introdurne di nuovi.

Se avessi un indice senza INCLUDE, non potresti sapere quali query interromperesti aggiungendo another_colsubito dopo Col1.

CREATE INDEX idx1 ON MyTable (Col1, Col2, Col3)

Cosa succede se aggiungi another_coltra Col1e Col2? Altre domande soffriranno?

Ci sono altri "vantaggi" delle INCLUDEcolonne chiave vs. se aggiungi quelle colonne solo per evitare di recuperarle dalla tabella . Tuttavia, considero l'aspetto della documentazione il più importante.

Per rispondere alla tua domanda:

quali linee guida suggeriresti per determinare se creare un indice di copertura con o senza la clausola INCLUDE?

Se aggiungi una colonna all'indice al solo scopo di renderla disponibile nell'indice senza visitare la tabella, inseriscila nella INCLUDEclausola.

Se l'aggiunta della colonna alla chiave dell'indice comporta ulteriori vantaggi (ad es. Per order byo perché può restringere l'intervallo dell'indice di lettura), aggiungerla alla chiave.

Puoi leggere una discussione più lunga su questo qui:

https://use-the-index-luke.com/blog/2019-04/include-columns-in-btree-indexes


2

Esiste un limite alla dimensione totale di tutte le colonne incluse nella definizione dell'indice. Detto questo, non ho mai dovuto creare un indice così ampio. Per me, il vantaggio maggiore è il fatto che è possibile coprire più query con un indice che ha incluso colonne in quanto non devono essere definite in alcun ordine particolare. Pensa è come un indice all'interno dell'indice. Un esempio potrebbe essere lo StoreID (dove StoreID ha una bassa selettività, il che significa che ogni negozio è associato a molti clienti) e quindi i dati demografici dei clienti (LastName, FirstName, DOB): se inserisci queste colonne in questo ordine (StoreID, LastName , FirstName, DOB), puoi cercare in modo efficiente solo i clienti per i quali conosci StoreID e LastName.

D'altra parte, definire l'indice su StoreID e includendo le colonne LastName, FirstName e DOB ti permetterebbe in sostanza di fare due predicati dell'indice di ricerca su StoreID e quindi cercare il predicato su una delle colonne incluse. Ciò ti consentirebbe di coprire tutte le possibili permutazioni di ricerca purché inizi con StoreID.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.