2009-08-20 2 views
333

Mentre studiavo per l'esame 70-433 ho notato che è possibile creare un indice di copertura in uno dei due modi seguenti.Perché utilizzare la clausola INCLUDE quando si crea un indice?

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

- O -

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

Il INCLUDE clausola è nuovo per me. Perché dovresti usarlo e quali linee guida suggeriresti nel determinare se creare un indice di copertura con o senza la clausola INCLUDE?

risposta

290

Se la colonna non si trova in WHERE/JOIN/GROUP BY/ORDER BY, ma solo nell'elenco delle colonne nella clausola SELECT.

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

Ciò significa che non è veramente utile per i predicati, l'ordinamento ecc. Come ho menzionato sopra. Tuttavia, si può essere utile se si dispone di una ricerca residua in poche righe dalla colonna chiave (s)

Another MSDN article with a worked example

+4

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

+1

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

+2

@ JMarsch: scusa per la risposta in ritardo, ma sì, questo è esattamente quello che è. – gbn

15

colonne di indice di base sono ordinati, ma incluso colonne non sono ordinati. Ciò consente di risparmiare risorse nel mantenimento dell'indice, consentendo comunque di fornire i dati nelle colonne incluse per coprire una query. Quindi, se vuoi coprire le query, puoi inserire i criteri di ricerca per individuare le righe nelle colonne ordinate dell'indice, ma poi "includere" colonne non ordinate aggiuntive con i dati non di ricerca. Aiuta sicuramente a ridurre la quantità di ordinamento e frammentazione nella manutenzione dell'indice.

176

Si utilizzerà INCLUDE per aggiungere una o più colonne al livello foglia di un indice non in cluster, se così facendo, è possibile "coprire" le query.

Immaginate di dover richiedere l'ID di un dipendente, l'ID reparto e il cognome.

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

Se vi capita di avere un indice non cluster (EmployeeID, DepartmentID), una volta a trovare i dipendenti per un determinato reparto, ora avete a che fare "segnalibro lookup" per vedere la scheda attuale dipendente completa , solo per ottenere la colonna del cognome. Questo può diventare piuttosto costoso in termini di prestazioni, se trovi molti dipendenti.

Se aveste compreso che cognome nell'indice:

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

poi tutte le informazioni necessarie sono disponibili nel livello foglia dell'indice non cluster. Semplicemente cercando l'indice non in cluster e trovando i tuoi dipendenti per un determinato dipartimento, hai tutte le informazioni necessarie e la ricerca dei segnalibri per ogni dipendente trovato nell'indice non è più necessaria -> risparmi un sacco di tempo.

Ovviamente, non è possibile includere ogni colonna in ogni indice non in cluster, ma se si hanno query che mancano solo una o due colonne da "coprire" (e che vengono utilizzate molto), può essere molto utile per INCLUDERE quelli in un indice adatto non cluster.

+17

Sei sicuro di voler usare questo indice? Perché EmployeeID? Hai solo bisogno di DepartmentID nelle colonne chiave? Sei stato citato qui come autorevole: http://stackoverflow.com/q/6187904/27535 – gbn

+3

La tua spiegazione è buona, ma in realtà non si allinea con il caso d'uso che hai delineato. Le colonne chiave dovrebbero essere sul filtro o chiavi 'JOIN' nella query, e' INCLUDE's devono essere i dati che stai recuperando ma non ordinando. – JNK

+10

Prima di tutto l'indice Employee (EmployeeID, DepartmentID) non verrà utilizzato per filtrare DepartmentID = 5. Poiché il suo ordine non corrisponde a – AnandPhadke

5

I motivi per cui (compresi i dati nel livello foglia dell'indice) sono stati ben spiegati.Il motivo per cui si danno due scosse a questo proposito è che quando si esegue la query, se non si dispone di colonne aggiuntive incluse (nuova funzionalità in SQL 2005), SQL Server deve passare 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 (la cache del buffer è specifica) quando le nuove pagine di dati vengono caricate in memoria, potenzialmente spingendo fuori dalla cache del buffer altri dati più spesso necessari.

+0

c'è un modo per dimostrare che sta effettivamente utilizzando meno memoria? è quello che mi aspetterei anch'io, ma mi sto facendo un po 'di staticità al lavoro – Asken

+0

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

2

Esiste un limite alla dimensione totale di tutte le colonne indicate 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 le colonne in quanto non devono essere definite in alcun ordine particolare. Pensare è come un indice all'interno dell'indice. Un esempio potrebbe essere lo StoreID (in cui StoreID è a bassa selettività che significa che ogni negozio è associato a molti clienti) e quindi i dati demografici dei clienti (LastName, FirstName, DOB): Se tali colonne sono state appena allineate in questo ordine (StoreID , LastName, FirstName, DOB), è possibile cercare in modo efficiente solo i clienti per i quali si conoscono StoreID e LastName.

D'altra parte, la definizione dell'indice su StoreID e l'inclusione di LastName, FirstName, colonne DOB consentono di eseguire essenzialmente due predicati di indice di ricerca su StoreID e quindi di cercare il predicato su una delle colonne incluse. Ciò ti consente di coprire tutte le possibili permute di ricerca purché inizi con StoreID.

4

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

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

16

Questa discussione si sta lasciando sfuggire il punto importante: La questione non è se il "non-chiave colonne" sono meglio di includere come indice-colonne o come incluso-colonne.

La domanda è quanto è costoso utilizzare il meccanismo di inclusione per includere colonne che sono non realmente necessarie nell'indice? (in genere non fa parte delle clausole where, ma spesso incluse in select). Così il vostro dilemma è sempre:

  1. Usa indice su ID1, ID2 ... idN solo o
  2. Usa indice su ID1, ID2 ... idN più includere col1, col2 ... Coln

Dove: ID1, ID2 ... idN sono colonne, spesso utilizzate in restrizioni e col1, col2 ... Cöln sono colonne spesso selezionati, ma in genere non utilizzato in restrizioni

(L'opzione per includere tutte queste colonne come parte della chiave dell'indice è sempre semplicemente sciocca (a meno che non vengano utilizzate anche nelle restrizioni) - perché sarebbe sempre più costosa da mantenere poiché l'indice deve essere aggiornato e ordinato anche quando i "tasti" non sono cambiati).

Quindi utilizzare l'opzione 1 o 2?

Risposta: Se la tabella è raramente aggiornato - per lo più inserita/cancellato dal - allora è relativamente poco costoso per usare il include-meccanismo per includere alcune "colonne caldi" (che sono spesso utilizzati in seleziona - ma non spesso usato su restrizioni) poiché gli inserimenti/eliminazioni richiedono che l'indice sia aggiornato/ordinato in ogni caso e quindi poco overhead aggiuntivo è associato all'archiviazione di alcune colonne aggiuntive mentre già aggiorna l'indice. L'overhead è la memoria aggiuntiva e la CPU utilizzata per memorizzare informazioni ridondanti sull'indice.

Se le colonne si considera da aggiungere come inclusi colonne sono spesso aggiornati (senza le index chiave-colonne in fase di aggiornamento) - o - se è così molti di loro che l'indice si avvicina a un copia del tuo tavolo - usa l'opzione 1 che suggerirei! Inoltre, se aggiungendo determinate colonne-colonna si scopre di non fare alcuna differenza in termini di prestazioni, potresti saltare l'idea di aggiungerle :) Verifica che siano utili!

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

Si noti che se una colonna - che si aggiunge come un incluso -column dell'indice - è utilizzato nel restrizione: Finché l'indice in quanto tale, può essere utilizzato (sulla base di restrizione contro Index chiave -columns), quindi SQL Server corrisponde alla restrizione di colonna rispetto all'indice (valori del nodo foglia) anziché utilizzare il metodo costoso attorno alla tabella stessa.