Devo indicizzare un campo di bit in SQL Server?


99

Ricordo di aver letto a un certo punto che non vale la pena indicizzare un campo con bassa cardinalità (un numero basso di valori distinti). Ammetto di non sapere abbastanza su come funzionano gli indici per capire perché.

Quindi cosa succede se ho una tabella con 100 milioni di righe e sto selezionando record in cui un campo di bit è 1? E diciamo che in qualsiasi momento ci sono solo una manciata di record in cui il campo di bit è 1 (invece di 0). Vale la pena indicizzare quel campo di bit o no? Perché?

Ovviamente posso solo testarlo e controllare il piano di esecuzione, e lo farò, ma sono anche curioso della teoria alla base. Quando è importante la cardinalità e quando no?


È una domanda comune? Potrebbe valerne la pena quando cerchi la "manciata" di record, ma non ti aiuterà molto sulle altre righe. Esistono altri modi per identificare i dati?
jason saldo

4
Anche se non penso che indicizzerei SOLO una colonna di bit da sola, è molto comune includere colonne di bit come parte di un indice composto. Un semplice esempio potrebbe essere un indice su ACTIVE, LASTNAME invece del solo cognome, quando la tua applicazione è quasi sempre alla ricerca di clienti attivi.
BradC

"Ricordo di aver letto a un certo punto che non vale la pena indicizzare un campo con una cardinalità bassa (un numero basso di valori distinti)" Questo perché SQL Server troverà quasi sempre più efficiente fare solo una scansione della tabella che leggere il indice. Quindi in pratica il tuo indice non verrà mai utilizzato ed è uno spreco mantenerlo. Come altri hanno già detto, potrebbe essere ok in un indice composto.
DJ.

5
Non sarei d'accordo. Se la tua distribuzione è 50/50, non useresti mai l'indice, poiché sarebbe solo più veloce eseguire una scansione della tabella. Tuttavia, se hai solo 5, 1 valori e 1 milione di valori 0, è molto probabile che utilizzi l'indice durante la ricerca di 1.
Kibbee

1
Nell'esempio che hai fornito, sarei più propenso a mettere LastName per primo. Dipende dal carico di lavoro della query specifica, ma in generale avere prima la colonna più selettiva significa che è più probabile che l'indice venga utilizzato.
Mitch Wheat

Risposte:


72

Considera cosa è un indice in SQL - e index è in realtà un pezzo di memoria che punta ad altri blocchi di memoria (cioè puntatori a righe). L'indice è suddiviso in pagine in modo che parti dell'indice possano essere caricate e scaricate dalla memoria a seconda dell'utilizzo.

Quando chiedi un set di righe, SQL utilizza l'indice per trovare le righe più rapidamente rispetto alla scansione della tabella (guardando ogni riga).

SQL ha indici cluster e non cluster. La mia comprensione degli indici cluster è che raggruppano valori di indice simili nella stessa pagina. In questo modo, quando si richiedono tutte le righe che corrispondono a un valore di indice, SQL può restituire quelle righe da una pagina di memoria in cluster. Questo è il motivo per cui provare a raggruppare l'indice di una colonna GUID è una cattiva idea: non si tenta di raggruppare valori casuali.

Quando si indicizza una colonna di numeri interi, l'indice di SQL contiene un insieme di righe per ogni valore di indice. Se hai un intervallo da 1 a 10, avrai 10 puntatori di indice. A seconda di quante righe ci sono, questo può essere impaginato in modo diverso. Se la tua query cerca l'indice che corrisponde a "1" e quindi dove Nome contiene "Fred" (supponendo che la colonna Nome non sia indicizzata), SQL ottiene molto rapidamente il set di righe che corrisponde a "1", quindi la tabella esegue la scansione per trovare il resto.

Quindi ciò che SQL sta realmente facendo è cercare di ridurre il working set (numero di righe) su cui deve iterare.

Quando indicizzi un campo di bit (o un intervallo ristretto), riduci il working set solo del numero di righe corrispondenti a quel valore. Se hai un piccolo numero di righe corrispondenti, ridurrebbe molto il tuo working set. Per un numero elevato di righe con distribuzione 50/50, potrebbe comportare un guadagno di prestazioni minimo rispetto al mantenimento dell'indice aggiornato.

Il motivo per cui tutti dicono di testare è perché SQL contiene un ottimizzatore molto intelligente e complesso che può ignorare un indice se decide che la scansione della tabella è più veloce, o può utilizzare un ordinamento, o può organizzare le pagine di memoria come preferisce.


Quindi sembra che se avessi solo una manciata di righe in cui il campo bit è 1 (ad esempio per tenere traccia di "IsProcessed"), allora un indice sarebbe buono perché le ordinerà per valore e quindi sarà in grado di selezionare il piccolo set di lavoro molto rapidamente. Se sei d'accordo, aggiungilo e lo accetterò.
jeremcc

2
Quello che intendo nel mio commento precedente è che questa affermazione: "Quando indicizzi un campo di bit (o un intervallo ristretto), riduci il working set solo a metà" non è vera se la distribuzione è fortemente ponderata verso un valore. Ma mi piace il resto della tua risposta, quindi se lo aggiusti, lo accetterò.
jeremcc

1
Fatto. Pensavo che per un milione di righe, un campo di bit avrebbe una distribuzione del 50%, ma hai ragione che per un particolare problema di spazio potrebbe ridurre di molto il working set.
Geoff Cox

Vale la pena esaminare i piani di esecuzione con e senza l'indice e vedere se l'indice viene utilizzato e se riduce effettivamente il costo delle query. Facile e scientifico!
onupdatecascade

Che dire dell'indicizzazione di un campo di bit + un altro campo? Per esempio. in un registro delle attività web, si indicizzerebbe il timestamp, ma un altro indice utile potrebbe trovarsi su un campo di bit "IsHTTPS" + timestamp, per visualizzare rapidamente tutte le azioni https. Anche questo sarebbe inefficiente?
ingrediente_15939

19

Mi sono imbattuto in questa domanda per mezzo di un'altra. Supponendo che la tua affermazione che solo una manciata di record assuma il valore di 1 (e che quelli siano quelli a cui sei interessato), un indice filtrato potrebbe essere una buona scelta. Qualcosa di simile a:

create index [IX_foobar] on dbo.Foobar (FooID) where yourBitColumn = 1

Ciò creerà un indice sostanzialmente più piccolo che l'ottimizzatore è abbastanza intelligente da utilizzare quando si tratta di un predicato nella query.


1
Vale la pena notare che il predicato nella query deve essere hardcoded sul valore nell'indice filtrato. Se si passa il valore in un parametro yourBitColumn = @value, l'ottimizzatore non può determinare se l'indice filtrato è utilizzabile.
geofftnz

2
Ci sono modi per aggirare questo problema, ma hai ragione; l'ottimizzatore necessita di una garanzia al momento della compilazione che i valori per tutti i predicati che corrispondono al predicato dell'indice filtrato siano statici / invarianti poiché è compito dell'ottimizzatore creare un piano generale che funzioni per qualsiasi insieme di parametri.
Ben Thul

9

100 milioni di record con solo pochi con il campo di bit impostato su 1? Sì, penso che l'indicizzazione del campo bit acceleri sicuramente l'interrogazione dei record bit = 1. Dovresti ottenere il tempo di ricerca logaritmico dall'indice e quindi toccare solo le poche pagine con record bit = 1. Altrimenti, dovresti toccare tutte le pagine della tabella dei 100 milioni di record.

Poi di nuovo, non sono assolutamente un esperto di database e potrebbe mancare qualcosa di importante.


8

Se la tua distribuzione è abbastanza nota e sbilanciata, come il 99% delle righe è bit = 1 e l'1% è bit = 0, quando esegui una clausola WHERE con bit = 1, una scansione completa della tabella avverrà più o meno nello stesso momento di l'indice di scansione. Se vuoi avere una query veloce dove bit = 0, il modo migliore che conosco è creare un indice filtrato, aggiungendo una clausola WHERE bit = 0. In questo modo, quell'indice memorizzerà solo la riga 1%. Quindi fare un WHERE bit = 0 lascerà semplicemente che Query Optimizer scelga quell'indice e tutte le righe da esso saranno bit = 0. Hai anche il vantaggio di avere una quantità molto piccola di spazio su disco richiesto confrontare un indice completo sul bit .


2
Se il 99% delle righe è bit = 1, l'ottimizzatore dovrebbe ignorare l'indice ed eseguire una scansione della tabella. L'utilizzo dell'indice sarà effettivamente peggiore di una scansione della tabella, almeno su un'unità rotazionale, più I / O e letture non consecutive dal disco. L'indice filtrato (equivalente Postgres: indice parziale) è la strada da percorrere. Immagino che perché sono passati anni dalla domanda, questa risposta non ha ottenuto i voti che meritava.
Andrew Lazarus

7

Anche se non penso che indicizzerei SOLO una colonna di bit da sola, è molto comune includere colonne di bit come parte di un indice composto.

Un semplice esempio potrebbe essere un indice su ACTIVE, LASTNAME invece del solo cognome, quando la tua applicazione è quasi sempre alla ricerca di clienti attivi.


7
Nell'esempio che hai fornito, sarei più propenso a mettere LastName per primo. Dipende dal carico di lavoro della query specifica, ma in generale avere prima la colonna più selettiva significa che è più probabile che l'indice venga utilizzato.
Mitch Wheat

7

Nel caso non l'avessi letto, Jason Massie ha recentemente scritto un articolo che ha discusso proprio di questo argomento.

http://statisticsio.com/Home/tabid/36/articleType/ArticleView/articleId/302/Never-Index-a-BIT.aspx

Modifica: posizione del nuovo articolo - http://sqlserverpedia.com/blog/sql-server-bloggers/never-index-a-bit

Wayback machine per la posizione dell'articolo precedentemente "Nuovo": http://web.archive.org/web/20120201122503/http://sqlserverpedia.com/blog/sql-server-bloggers/never-index-a-bit/

La nuova posizione di SQL Server Pedia è Toadworld, che contiene un nuovo articolo di Kenneth Fisher che discute di questo argomento:

http://www.toadworld.com/platforms/sql-server/b/weblog/archive/2014/02/17/dba-myths-an-index-on-a-bit-column-will-never-be- used.aspx

macchina wayback: http://web.archive.org/web/20150508115802/http://www.toadworld.com/platforms/sql-server/b/weblog/archive/2014/02/17/dba-myths-an -indice-su-una-colonna-bit-non-sarà-mai-usata.aspx


questo articolo non è più visibile
Homer6

@ Homer6 Ho aggiunto un collegamento a quella che sembra la nuova casa per questo articolo.
Jeff

Il nuovo collegamento va alla homepage di Toad World.
N Ovest

Ho trovato l'articolo utilizzando la macchina Wayback e ho trovato un nuovo articolo correlato. Spero che questo ti aiuti.
Jeff

2

Ovviamente vale la pena, soprattutto se è necessario recuperare i dati in base a quel valore. Sarebbe simile all'utilizzo di una matrice sparsa invece di utilizzare una matrice normale.

Ora con SQL 2008 puoi utilizzare le funzioni di partizionamento e puoi filtrare i dati che vanno in un indice. Lo svantaggio per le versioni precedenti sarebbe che l'indice sarebbe stato creato per tutti i dati, ma questo può essere ottimizzato memorizzando i valori interessanti in un gruppo di file separato.


2

Come altri hanno già detto, vorrai misurarlo. Non ricordo dove ho letto questo, ma una colonna deve avere una cardinalità molto alta (intorno al 95%) affinché un indice sia efficace. Il miglior test per questo sarebbe costruire l'indice ed esaminare i piani di esecuzione per i valori 0 e 1 del campo BIT. Se vedi un'operazione di ricerca dell'indice nel piano di esecuzione, sai che il tuo indice verrà utilizzato.

La cosa migliore da fare sarebbe provare con una tabella SELECT * FROM di base WHERE BitField = 1; eseguire query e sviluppare lentamente la funzionalità da lì passo dopo passo fino a ottenere una query realistica per la tua applicazione, esaminando il piano di esecuzione ad ogni passaggio per assicurarti che l'indice seek sia ancora in uso. Certo, non vi è alcuna garanzia che questo piano di esecuzione verrà utilizzato nella produzione, ma ci sono buone probabilità che lo sia.

Alcune informazioni possono essere trovate sui forum sql-server-performance.com e nell'articolo di riferimento


Non è tanto la cardinalità della colonna nel suo insieme che conta. È la selettività della clausola WHERE. Quindi, se ci sono poche colonne con valore 1, può comunque essere utile indicizzarle. Se è 50/50 (ad esempio maschio / femmina), non ne vale la pena.
WW.

2

"Ricordo di aver letto a un certo punto che non vale la pena indicizzare un campo con bassa cardinalità (un numero basso di valori distinti)"

Questo perché SQL Server troverà quasi sempre più efficiente fare solo una scansione della tabella che leggere l'indice. Quindi in pratica il tuo indice non verrà mai utilizzato ed è uno spreco mantenerlo. Come altri hanno già detto, potrebbe essere ok in un indice composto.


2

Se il tuo obiettivo è eseguire query per i record in cui il valore del campo di bit è uguale a "1" più velocemente, potresti provare una visualizzazione indicizzata della tabella di base che contiene solo record in cui il tuo campo di bit è uguale a "1". Nell'edizione Enterprise, se una query può utilizzare una vista indicizzata invece di una tabella specificata per migliorare le prestazioni della query, utilizzerà la vista. In teoria, ciò aumenterebbe la velocità delle query selezionate che cercano solo record con un valore di campo di bit "1".

http://www.microsoft.com/technet/prodtechnol/sql/2005/impprfiv.mspx

Tutto ciò presuppone che tu sia Microsoft SQL Server 2005 Enterprise. Lo stesso potrebbe valere per il 2008, non ho familiarità con quella versione.


2

Se vuoi sapere se un indice ha gli effetti che desideri: prova e riprova.

In generale, non vuoi un indice che non restringa abbastanza la tua tabella, a causa del costo per mantenere un indice. (costo> profitto). Ma se l'indice nel tuo caso taglierà il tavolo a metà, potresti guadagnare qualcosa ma metterlo sul tavolo. Tutto dipende dalla dimensione / struttura esatta della tabella e da come la si utilizza (numero di letture / scritture).


1

Di per sé no, in quanto si traduce in una selettività molto ridotta. Come parte di un indice composto. molto probabilmente, ma solo dopo altre colonne di uguaglianza.


1

Non è possibile indicizzare un campo di bit in SQL Server 2000, come indicato all'epoca nella documentazione in linea:

po

Tipo di dati intero 1, 0 o NULL.

Osservazioni

Le colonne di tipo bit non possono contenere indici.

Sì, se hai solo una manciata di righe, su milioni, un indice ti aiuterà. Ma se vuoi farlo in questo caso devi rendere la colonna a tinyint.

Nota : Enterprise Manager non ti consentirà di creare un indice su una colonna di bit. Se lo desideri, puoi comunque creare manualmente un indice su una colonna di bit:

CREATE INDEX IX_Users_IsActiveUsername ON Users
(
   IsActive,
   Username
)

Ma SQL Server 2000 non utilizzerà effettivamente tale indice, eseguendo una query in cui l'indice sarebbe un candidato perfetto, ad esempio:

SELECT TOP 1 Username 
FROM Users
WHERE IsActive = 0

SQL Server 2000 eseguirà invece una scansione della tabella, agendo come se l'indice non esistesse nemmeno. Se si modifica la colonna in un tinyint, SQL Server 2000 eseguirà una ricerca dell'indice. Inoltre, la seguente query non coperta:

SELECT TOP 1 * 
FROM Users
WHERE IsActive = 0

Eseguirà una ricerca di indice, seguita da una ricerca di segnalibri.


SQL Server 2005 ha un supporto limitato per gli indici sulle colonne di bit. Per esempio:

SELECT TOP 1 Username 
FROM Users
WHERE IsActive = 0

farà sì che un indice cerchi attraverso l'indice di copertura. Ma il caso non coperto:

SELECT TOP 1 * 
FROM Users
WHERE IsActive = 0

non provocherà una ricerca dell'indice seguita da una ricerca del segnalibro, eseguirà una scansione della tabella (o scansione dell'indice cluster), invece di eseguire la ricerca dell'indice seguita da una ricerca del segnalibro.

Verificato da sperimentazione e osservazione diretta.


Cordiali saluti, SQL Server 2005 Management Studio ti consente di farlo.
jeremcc

La mia copia di SQL Server 2000 mi ha permesso di impostare un indice su una colonna di bit.
Kibbee

La mia copia di SQL Server 2000 non mi consente di impostare un indice su una colonna di bit.
Ian Boyd

1

risposta molto tardi ...

Sì, può essere utile secondo il team SQL CAT (aggiornato, è stato consolidato)


1
Il collegamento sembra essere morto ora. Tuttavia, quel post sembra essere stato consolidato insieme a molti altri in un e-book . La sezione a cui si fa riferimento inizia a pagina 86. L'e-book può essere scaricato da SQLCAT.com eBooks al link "SQLCAT's Guide to Relational Engine".
mwolfe02

0

È una domanda comune? Potrebbe valerne la pena quando cerchi la "manciata" di record, ma non ti aiuterà molto sulle altre righe. Esistono altri modi per identificare i dati?


0

La cardinalità è un fattore, l'altro è il modo in cui l'indice divide i dati. Se hai circa metà 1 e metà 0, allora sarà d'aiuto. (Supponendo che quell'indice sia un percorso migliore da scegliere rispetto a qualche altro indice). Tuttavia, quanto spesso inserisci e aggiorni? L'aggiunta di indici per le prestazioni SELECT danneggia anche le prestazioni INSERT, UPDATE e DELETE, quindi tienilo a mente.

Direi, se l'1 allo 0 (o viceversa) non è migliore del 75% al ​​25%, non preoccuparti.


1
Non sarei d'accordo. Se la tua distribuzione è 50/50, non useresti mai l'indice, poiché sarebbe solo più veloce eseguire una scansione della tabella. Tuttavia, se hai solo 5, 1 valori e 1 milione di valori 0, è molto probabile che utilizzi l'indice durante la ricerca di 1.
Kibbee

0

misurare il tempo di risposta prima e dopo e vedere se ne vale la pena; teoricamente dovrebbe migliorare le prestazioni per le query che utilizzano i campi indicizzati ma in realtà dipende dalla distribuzione dei valori vero / falso e dagli altri campi coinvolti nelle query di cui sei preoccupato


0

Ian Boyd ha ragione quando dice che non è possibile farlo tramite Enterprise Manager per SQL 2000 (vedere la sua nota sulla creazione tramite T-SQL.


0

Devi essere intelligente qui per interrogare, devi conoscere il valore di caricamento sulla tua colonna se il carico di true è più presente nel tuo sistema e vuoi controllare tutti i valori veri scrivendo la tua query per verificare non falso .. aiuterà molto , è solo un trucco.

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.