Quanto è importante l'ordine delle colonne negli indici?


173

Ho sentito che dovresti inserire colonne che saranno le più selettive all'inizio della dichiarazione dell'indice. Esempio:

CREATE NONCLUSTERED INDEX MyINDX on Table1
(
   MostSelective,
   SecondMost,
   Least
)

Prima di tutto, è quello che sto dicendo corretto? In tal caso, è probabile che vedo grandi differenze nelle prestazioni riorganizzando l'ordine delle colonne nel mio indice o è più una pratica "piacevole da fare"?

Il motivo che sto chiedendo è perché dopo aver inserito una query attraverso il DTA mi ha raccomandato di creare un indice che contenesse quasi tutte le stesse colonne di un indice esistente, in un ordine diverso. Stavo pensando di aggiungere solo le colonne mancanti all'indice esistente e di chiamarlo bene. Pensieri?

Risposte:


193

Guarda un indice come questo:

Cols
  1   2   3
-------------
|   | 1 |   |
| A |---|   |
|   | 2 |   |
|---|---|   |
|   |   |   |
|   | 1 | 9 |
| B |   |   |
|   |---|   |
|   | 2 |   |
|   |---|   |
|   | 3 |   |
|---|---|   |

Vedi in che modo la limitazione su A per prima, poiché la tua prima colonna elimina più risultati rispetto alla limitazione sulla seconda colonna per prima? È più facile se immagini come attraversare l'indice, la colonna 1, quindi la colonna 2, ecc ... vedi che la maggior parte dei risultati nel passaggio del pugno rende il secondo passo molto più veloce.

Un altro caso, se si esegue una query sulla colonna 3, l'ottimizzatore non userebbe nemmeno l'indice, perché non è affatto utile nel restringere i set di risultati. Ogni volta che sei in una query, restringere il numero di risultati da affrontare prima del passaggio successivo significa migliorare le prestazioni.

Poiché anche l'indice viene archiviato in questo modo, non è possibile eseguire il backtracking dell'indice per trovare la prima colonna quando si esegue una query su di esso.

In breve: No, non è per lo spettacolo, ci sono reali vantaggi in termini di prestazioni.


13
Nell'immagine sopra, tieni presente che quell'indice sarebbe utile solo se la colonna 1 fosse specificata nella query. Se la tua query specifica solo la colonna 2 nel predicato Join o Search, non sarebbe utile. Quindi l'ordine conta anche lì. Forse è ovvio, ma volevo menzionarlo.
CodeCowboyOrg

3
Inoltre, tieni presente che supponi che il tuo indice sia come l'immagine sopra e che la tua query filtri su column1 e column2, ma column2 sia più unica e ciò su cui vuoi veramente filtrare sia effettivamente column2, quindi è più vantaggioso avere un indice in cui la colonna 2 è la prima. Questo può sembrare controintuitivo, ma tieni presente che un indice è memorizzato su più pagine ed è un albero con un intervallo di valori, mentre la colonna 1 sopra nega 1/2 delle possibilità, l'indice sa già a quale pagina dell'indice andare direttamente per il Valore Column2, non è necessario Colonna 1 per restringere il set.
CodeCowboyOrg

4
Questa immagine non è una rappresentazione accurata di come sono strutturati o navigati gli indici. Ho inviato una risposta rettificando questo stackoverflow.com/a/39080819/73226
Martin Smith,

6
@MartinSmith Non sono d'accordo sul fatto che sia inaccurato. È certamente molto semplificato, il che era il mio intento. La tua risposta è molto più dettagliata dei livelli, tuttavia, è apprezzata, per coloro che vogliono approfondire. Se guardi l'immagine dell'albero, vedrai quello che sto illustrando in un modo molto semplice. Questo non è molto unico o addirittura specifico di SQL; L'indicizzazione B-tree è piuttosto comune in così tante cose.
Nick Craver

@MartinSmith Non condivido nemmeno che sia inaccurato, quello che stai descrivendo è il comportamento standard di come arrivare all'indice di copertura: la selettività è molto più importante una volta che esegui query sul range in quanto ciò minimizza il numero di pagine indice che l'ottimizzatore deve scansionare; questo può essere significativo nelle tabelle di grandi dimensioni con milioni di righe
Paul Hatcher,

127

L'ordine delle colonne è fondamentale. Adesso quale ordine è corretto dipende da come lo interrogherai. Un indice può essere utilizzato per eseguire una ricerca esatta o una scansione dell'intervallo. Una ricerca esatta è quando vengono specificati i valori per tutte le colonne dell'indice e la query arriva esattamente sulla riga interessata. Per le ricerche, l'ordine delle colonne è irrilevante. Una scansione dell'intervallo è quando vengono specificate solo alcune colonne, e in questo caso quando l'ordine diventa importante. SQL Server può utilizzare un indice per una scansione dell'intervallo solo se viene specificata la colonna più a sinistra, quindi solo se viene specificata la colonna più a sinistra successiva e così via. Se si dispone di un indice su (A, B, C), è possibile utilizzare l'intervallo di scansione per A=@a, per A=@a AND B=@bma non per B=@b, per C=@cB=@b AND C=@c. Il caso A=@a AND C=@cè misto, come nelA=@aparte utilizzerà l'indice, ma C=@cnon (la query eseguirà la scansione di tutti i valori B per A=@a, non salterà a C=@c). Altri sistemi di database hanno il cosiddetto operatore 'skip scan' che può trarre vantaggio dalle colonne interne in un indice quando le colonne esterne non sono specificate.

Con questa conoscenza a portata di mano è possibile rivedere le definizioni dell'indice. Un indice (MostSelective, SecondMost, Least)attivo sarà efficace solo quando MostSelectiveviene specificata la colonna. Ma essendo il più selettivo, la rilevanza delle colonne interne si ridurrà rapidamente. Molto spesso scoprirai che un indice migliore è attivo (MostSelective) include (SecondMost, Least)o attivo (MostSelective, SecondMost) include (Least). Poiché le colonne interne sono meno rilevanti, posizionare le colonne a bassa selettività in tali posizioni giuste nell'indice non fa altro che rumore per una ricerca, quindi ha senso spostarle dalle pagine intermedie e tenerle solo sulle pagine foglia, per scopi di copertura della query. In altre parole, spostali su INCLUDE. Ciò diventa più importante Leastall'aumentare della dimensione della colonna. L'idea è che questo indice può beneficiare solo delle query specificateMostSelective sia come valore esatto che come intervallo e quella colonna, essendo la più selettiva, limita già in larga misura le righe candidate.

D'altra parte un indice su (Least, SecondMost, MostSelective)può sembrare un errore, ma in realtà è un indice abbastanza potente. Poiché ha la Leastcolonna come query più esterna, può essere utilizzata per le query che devono aggregare i risultati su colonne a bassa selettività. Tali query sono prevalenti in OLAP e nei data warehouse di analisi, ed è proprio qui che tali indici hanno un ottimo caso. Tali indici in realtà formano eccellenti indici raggruppati , proprio perché organizzano il layout fisico su grandi blocchi di righe correlate (stesso Leastvalore, che di solito indicano una sorta di categoria o tipo) e facilitano le query di analisi.

Quindi, sfortunatamente, non esiste un ordine "corretto". Non dovresti seguire alcuna ricetta di cookie cutter ma invece analizza il modello di query che utilizzerai su quelle tabelle e decidi quale ordine di colonna dell'indice è giusto.


3
Splendida risposta come al solito Remus. Leggerò più volte il tuo terzo paragrafo e darò seguito. Sospetto che possa essere esattamente quello che devo fare.
Abe Miessler,

"SQL Server può utilizzare un indice per una scansione dell'intervallo solo se viene specificata la colonna più a sinistra, quindi solo se viene specificata la colonna più a sinistra successiva e così via." Questo è esattamente ciò che mancava dalla mia comprensione, grazie! Non sapevo che le scansioni dell'intervallo potessero essere eseguite solo sulla colonna dell'indice utilizzata più a destra, ma ora che lo faccio ha molto senso.
Allon Guralnek,

Questa spiegazione è applicabile per Oracle DB?
un altro

1
@Roizpi Sì, praticamente qualsiasi database di relazioni con Indexes funziona allo stesso modo o in modo molto simile.
Tatranskymedved il

45

Come dice Remus, dipende dal carico di lavoro.

Voglio affrontare un aspetto fuorviante della risposta accettata però.

Per le query che eseguono una ricerca di uguaglianza su tutte le colonne dell'indice non vi sono differenze significative.

Il seguito crea due tabelle e le popola con dati identici. L'unica differenza è che uno ha i tasti ordinati dal più al meno selettivo e l'altro il contrario.

CREATE TABLE Table1(MostSelective char(800), SecondMost TINYINT, Least  CHAR(1), Filler CHAR(4000) null);
CREATE TABLE Table2(MostSelective char(800), SecondMost TINYINT, Least  CHAR(1), Filler CHAR(4000) null);

CREATE NONCLUSTERED INDEX MyINDX on Table1(MostSelective,SecondMost,Least);
CREATE NONCLUSTERED INDEX MyINDX2 on Table2(Least,SecondMost,MostSelective);

INSERT INTO Table1 (MostSelective, SecondMost, Least)
output inserted.* into Table2
SELECT TOP 26 REPLICATE(CHAR(number + 65),800), number/5, '~'
FROM master..spt_values
WHERE type = 'P' AND number >= 0
ORDER BY number;

Ora eseguendo una query su entrambe le tabelle ...

SELECT *
FROM   Table1
WHERE  MostSelective = REPLICATE('P', 800)
       AND SecondMost = 3
       AND Least = '~';

SELECT *
FROM   Table2
WHERE  MostSelective = REPLICATE('P', 800)
       AND SecondMost = 3
       AND Least = '~'; 

... Entrambi usano una multa indice ed entrambi hanno lo stesso costo esatto.

inserisci qui la descrizione dell'immagine

L'arte ASCII nella risposta accettata non è in realtà la struttura degli indici. Le pagine di indice per Table1 sono rappresentate di seguito (fare clic sull'immagine per aprirla a schermo intero).

inserisci qui la descrizione dell'immagine

Le pagine dell'indice contengono righe contenenti l'intera chiave (in questo caso in realtà è stata aggiunta una colonna chiave aggiuntiva per l'identificatore di riga poiché l'indice non è stato dichiarato come univoco ma è possibile ignorare ulteriori informazioni al riguardo ).

Per la query sopra SQL Server non interessa la selettività delle colonne. Esegue una ricerca binaria della pagina principale e scopre che la chiave (PPP...,3,~ ) è >=(JJJ...,1,~ )e < (SSS...,3,~ )quindi dovrebbe leggere la pagina 1:118. Quindi effettua una ricerca binaria delle voci chiave in quella pagina e individua la pagina foglia su cui viaggiare.

La modifica dell'indice in ordine di selettività non influisce né sul numero previsto di confronti chiave dalla ricerca binaria né sul numero di pagine che devono essere esplorate per effettuare una ricerca dell'indice. Nella migliore delle ipotesi, potrebbe accelerare leggermente il confronto chiave stesso.

A volte, tuttavia, ordinare prima l'indice più selettivo avrà senso per altre query nel carico di lavoro.

Ad esempio, se il carico di lavoro contiene query di entrambi i seguenti moduli.

SELECT * ... WHERE  MostSelective = 'P'

SELECT * ...WHERE Least = '~'

Gli indici sopra non riguardano nessuno dei due. MostSelectiveè abbastanza selettivo da rendere utile un piano con una ricerca e ricerche, ma la query Leastnon lo è.

Tuttavia, questo scenario (ricerca dell'indice non coprente su un sottoinsieme delle colonne principali di un indice composito) è solo una possibile classe di query che può essere aiutata da un indice. Se non cerchi mai da MostSelectivesolo o una combinazione di MostSelective, SecondMoste cerchi sempre da una combinazione di tutte e tre le colonne, questo vantaggio teorico è inutile per te.

Al contrario query come

SELECT MostSelective,
       SecondMost,
       Least
FROM   Table2
WHERE  Least = '~'
ORDER  BY SecondMost,
          MostSelective 

Sarebbe aiutato dall'ordine inverso di quello comunemente prescritto - poiché copre la query, può supportare una ricerca e restituisce le righe nell'ordine desiderato per l'avvio.

Quindi questo è un consiglio spesso ripetuto, ma al massimo è un'euristica sul potenziale beneficio di altre query - e non è un sostituto per guardare effettivamente il tuo carico di lavoro.


31

dovresti inserire colonne che saranno le più selettive all'inizio della dichiarazione dell'indice.

Corretta. Gli indici possono essere composti - composti da più colonne - e l'ordine è importante a causa del principio più a sinistra. Il motivo è che il database controlla l'elenco da sinistra a destra e deve trovare un riferimento di colonna corrispondente corrispondente all'ordine definito. Ad esempio, avere un indice su una tabella di indirizzi con colonne:

  • Indirizzo
  • Città
  • Stato

Qualsiasi query che utilizza la addresscolonna può utilizzare l'indice, ma se la query ha solo citye / o stateriferimenti, l'indice non può essere utilizzato. Questo perché la colonna più a sinistra non è referenziata. Le prestazioni delle query dovrebbero indicare quale è ottimale: singoli indici o più compositi con ordini diversi. Buona lettura: The Tipping Point , di Kimberley Tripp


E se fosse stata utilizzata solo la colonna più a destra? Quindi una query ha utilizzato Indirizzo e città, ma NON stato. L'indice verrebbe utilizzato allora?
Abe Miessler,

@Abe: l'estrema destra non verrebbe utilizzata - devi soddisfare l'ordine dell'indice partendo da sinistra. Manca uno, non posso usarlo.
OMG Pony

4
@Abe: se si esegue una query su Indirizzo e città, ma NON stato - quindi sì, verrà utilizzato l'indice. In altre parole, il database è in grado di utilizzare indici parziali per soddisfare una richiesta, purché sia ​​in grado di iniziare da sinistra di un indice e spostarsi a destra utilizzando i campi su cui viene eseguita la query. Se, tuttavia, hai eseguito una query utilizzando Indirizzo e Stato, ma NON città, potrebbe comunque utilizzare l'indice, ma non sarà altrettanto efficiente, perché ora è in grado di utilizzare solo la parte Indirizzo dell'indice (b / c successivo è città e non viene utilizzato nella query).
JaredC,

6

Tutte le altre risposte sono sbagliate.

La selettività delle singole colonne in un indice composito non ha importanza quando si seleziona l'ordine.

Ecco il semplice processo di pensiero: in effetti, un indice è la concatenazione delle colonne coinvolte.

Dando questa logica, l'unica differenza sta nel confrontare due "stringhe" che differiscono prima rispetto alla successiva nella stringa. Questa è una piccola parte del costo totale. Non esiste un "primo passaggio / secondo passaggio", come indicato in una risposta.

Quindi, quale ordine dovrebbe essere usato?

  1. Inizia con le colonne testate con =, in qualsiasi ordine.
  2. Quindi virare su una colonna di distanza.

Ad esempio, la colonna di selettività molto bassa deve venire prima in questo:

WHERE deleted = 0  AND  the_datetime > NOW() - INTERVAL 7 DAY
INDEX(deleted, the_datetime)

Lo scambio dell'ordine nell'indice lo ignorerebbe totalmente deleted.

(Ci sono molte più regole per ordinare le colonne.)


Il voto negativo è perché ho torto? O perché ho una forte opinione? O qualcos'altro?
Rick James,

non è stato il mio downvote, ma cancellato = 0 per me sembra che non sia bassa selettività? Immagino che sarebbe la maggior parte delle righe nella tabella.
Greg

@Greg - Penso che significhi "bassa selettività" - Cioè, l'utilizzo deletednon aiuta molto a filtrare le righe indesiderate. Hai un esempio migliore? (È quello che mi è venuto in mente quando ho scritto la Risposta.)
Rick James,

Incomprensione da parte mia.
Greg

1
@ClickOk - Grazie. Il mio libro di cucina fornisce alcune informazioni di base: mysql.rjweb.org/doc.php/index_cookbook_mysql
Rick James
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.