Versione breve: cercare è molto meglio
Versione meno breve: la ricerca è generalmente molto migliore, ma molte ricerche (causate ad esempio da una cattiva progettazione di query con cattive sottoquery correlate o perché stai facendo molte query in un'operazione del cursore o in un altro ciclo) possono essere peggiori di un scansione, specialmente se la tua query potrebbe finire per restituire dati dalla maggior parte delle righe nella tabella interessata.
Aiuta a coprire l'intera famiglia per le operazioni di ricerca dei dati per comprendere appieno le implicazioni delle prestazioni.
Scansioni delle tabelle: senza indici rilevanti per la query, il planner è costretto a utilizzare una scansione delle tabelle, il che significa che ogni riga viene esaminata. Ciò può comportare la lettura dal disco di ogni pagina relativa ai dati della tabella, che è spesso il caso peggiore. Si noti che per alcune query verrà utilizzata una scansione della tabella anche quando è presente un indice utile; ciò di solito perché i dati nella tabella sono così piccoli che è più fastidioso attraversare gli indici (in questo caso ci si aspetterebbe che pianificare di cambiare con l'aumentare dei dati, presupponendo che la misura della selettività dell'indice sia buona).
Scansioni dell'indice con ricerche di righe: senza l'indice che può essere utilizzato direttamente per una ricerca, ma è presente un indice contenente le colonne giuste, è possibile utilizzare una scansione dell'indice. Ad esempio, se si dispone di una tabella di grandi dimensioni con 20 colonne con un indice su column1, col2, col3 e si genera SELECT col4 FROM exampletable WHERE col2=616
, in questo caso scansionare l'indice su query col2
è meglio che scansionare l'intera tabella. Una volta trovate le righe corrispondenti, le pagine dei dati devono essere lette per prelevare col4 per l'output (o ulteriore unione), che è la fase di "ricerca dei segnalibri" quando la si vede nei piani di query.
Scansioni dell'indice senza ricerche diSELECT col1, col2, col3 FROM exampletable WHERE col2=616
righe : se l'esempio precedente era lo sforzo extra per leggere le pagine di dati non è necessario: una volta col2=616
trovata la corrispondenza delle righe di indice, tutti i dati richiesti sono noti. Questo è il motivo per cui a volte vedi colonne che non verranno mai cercate, ma che probabilmente verranno richieste per l'output, aggiunte alla fine degli indici: può salvare le ricerche di righe. Quando si aggiungono colonne a un indice solo per questo motivo e per questo motivo, aggiungerle con la INCLUDE
clausola per indicare al motore che non è necessario ottimizzare il layout dell'indice per le query basate su queste colonne (questo può accelerare gli aggiornamenti effettuati su quelle colonne) . Le scansioni dell'indice possono derivare anche da query senza clausole di filtro: SELECT col2 FROM exampletable
eseguiranno la scansione di questo indice di esempio anziché delle pagine della tabella.
Ricerche indice (con o senza ricerche di righe) : in una ricerca non viene considerato tutto l'indice. Per la query, SELECT * FROM exampletable WHERE c1 BETWEEN 1234 AND 4567
il motore di query può trovare la prima riga che corrisponde eseguendo una ricerca basata sull'albero sull'indice, c1
quindi può navigare nell'indice fino a quando non arriva alla fine dell'intervallo (questo è lo stesso con una query per c1=1234
quanto vi potrebbero essere molte righe corrispondono alla condizione anche per un =
funzionamento). Ciò significa che devono essere lette solo le pagine dell'indice pertinenti (più alcune necessarie per la ricerca iniziale) anziché ogni pagina dell'indice (o tabella).
Indici cluster: con un indice cluster i dati della tabella vengono archiviati nei nodi foglia di quell'indice anziché in una struttura heap separata. Ciò significa che non sarà mai necessario effettuare ulteriori ricerche di riga dopo aver trovato le righe utilizzando tale indice, indipendentemente dalle colonne necessarie [a meno che non si disponga di dati fuori pagina come TEXT
colonne o VARCHAR(MAX)
colonne contenenti dati lunghi].
Per questo motivo puoi avere un solo indice cluster [1] , l'indice cluster è la tua tabella invece di avere una struttura heap separata, quindi se usi uno [2] scegli dove metterlo con cura per ottenere il massimo guadagno.
Si noti inoltre che l'indice cluster perché la "chiave di clustering" per la tabella ed è incluso in ogni indice non cluster sulla tabella, quindi un indice cluster ampio non è generalmente una buona idea.
[1] In realtà, si può effettivamente avere più indici cluster attraverso la definizione di indici non cluster che coprono o che contengono ogni colonna sul tavolo, ma questo è probabile che sia uno spreco di spazio ha un impatto sulle prestazioni in scrittura, quindi se si considera farlo assicurarsi hai davvero bisogno.
[2] Quando dico "se si utilizza un indice cluster", faccio notare che è generalmente consiglia di fare avere uno su ogni tavolo. Ci sono eccezioni come per tutte le regole empiriche, le tabelle che vedono poco altro che inserimenti di massa e letture non ordinate (forse tabelle di stadiazione per i processi ETL) sono il contro esempio più comune.
Punto aggiuntivo: scansioni incomplete:
È importante ricordare che, a seconda del resto della query, una scansione di tabella / indice potrebbe non eseguire la scansione dell'intera tabella, se la logica consente al piano di query di essere in grado di interromperlo anticipatamente. L'esempio più semplice di questo è SELECT TOP(1) * FROM HugeTable
: se guardi il piano di query per questo vedrai che una sola riga è stata restituita dalla scansione e se guardi le statistiche IO ( SET STATISTICS IO ON; SELECT TOP(1) * FROM HugeTable
) vedrai che legge solo un numero molto piccolo di pagine (forse solo una).
Lo stesso può accadere se il predicato di una clausola WHERE
o JOIN ... ON
può essere eseguito contemporaneamente alla scansione che è l'origine se i suoi dati. Il planner / runner delle query a volte può essere molto intelligente nel reindirizzare i predicati verso le origini dati per consentire la terminazione anticipata delle scansioni in questo modo (e talvolta si può essere intelligenti nel riorganizzare le query per aiutarlo a farlo!). Mentre i dati scorrono da destra a sinistra secondo le frecce nella visualizzazione del piano di query standard, la logica viene eseguita da sinistra a destra e ogni passaggio (da destra a sinistra) non viene necessariamente eseguito fino al completamento prima che possa iniziare il successivo. Nel semplice esempio sopra riportato, se si considera ciascun blocco nel piano di query come agente, l' SELECT
agente richiede TOP
all'agente una riga che a sua volta chiede alTABLE SCAN
agente per uno, quindi l' SELECT
agente ne chiede un altro, ma l' TOP
agente sa che non è necessario non si preoccupa nemmeno di chiedere al lettore di tabelle, l' SELECT
agente riceve una risposta "non più pertinente" e sa tutto il lavoro svolto. Molte operazioni bloccare questo tipo di ottimizzazione, naturalmente, così spesso negli esempi più complessi di una scansione di tabella / indice in realtà non leggere ogni riga, ma fate attenzione a non saltare alla conclusione che ogni scansione deve essere un'operazione costosa.