Soll ich ein Bitfeld in SQL Server indizieren?


99

Ich erinnere mich, dass ich an einer Stelle gelesen habe, dass es sich nicht wirklich lohnt, ein Feld mit geringer Kardinalität (eine geringe Anzahl unterschiedlicher Werte) zu indizieren. Ich gebe zu, ich weiß nicht genug darüber, wie Indizes funktionieren, um zu verstehen, warum das so ist.

Was ist, wenn ich eine Tabelle mit 100 Millionen Zeilen habe und Datensätze auswähle, bei denen ein Bitfeld 1 ist? Nehmen wir an, es gibt zu jedem Zeitpunkt nur eine Handvoll Datensätze, bei denen das Bitfeld 1 ist (im Gegensatz zu 0). Lohnt es sich, dieses Bitfeld zu indizieren oder nicht? Warum?

Natürlich kann ich es einfach testen und den Ausführungsplan überprüfen, und das werde ich tun, aber ich bin auch neugierig auf die Theorie dahinter. Wann ist Kardinalität wichtig und wann nicht?


Ist das eine häufige Abfrage? Es kann sich lohnen, wenn Sie nach einer "Handvoll" Datensätzen suchen, hilft Ihnen aber in den anderen Zeilen nicht viel. Gibt es andere Möglichkeiten, die Daten zu identifizieren?
Jason Saldo

4
Obwohl ich nicht glaube, dass ich NUR eine Bitspalte selbst indizieren würde, ist es sehr üblich, Bitspalten als Teil eines zusammengesetzten Index einzuschließen. Ein einfaches Beispiel wäre ein Index für ACTIVE, LASTNAME anstelle von nur Nachname, wenn Ihre Anwendung fast immer nach aktiven Kunden sucht.
BradC

"Ich erinnere mich, dass ich an einem Punkt gelesen habe, dass es sich nicht wirklich lohnt, ein Feld mit geringer Kardinalität (eine geringe Anzahl unterschiedlicher Werte) zu indizieren." Das liegt daran, dass SQL Server es fast immer effizienter findet, nur einen Tabellenscan durchzuführen, als das zu lesen Index. Im Grunde genommen wird Ihr Index niemals verwendet und es ist eine Verschwendung, ihn zu pflegen. Wie andere gesagt haben, könnte es in einem zusammengesetzten Index in Ordnung sein.
DJ.

5
Ich würde nicht zustimmen. Wenn Ihre Verteilung 50/50 ist, würden Sie den Index niemals verwenden, da es nur schneller wäre, einen Tabellenscan durchzuführen. Wenn Sie jedoch nur 5, 1 Werte und 1 Million 0 Werte haben, ist es sehr wahrscheinlich, dass der Index bei der Suche nach 1 verwendet wird.
Kibbee

1
In dem Beispiel, das Sie gegeben haben, wäre ich eher geneigt, den Nachnamen an die erste Stelle zu setzen. Dies hängt von der spezifischen Abfragearbeitslast ab. Wenn jedoch im Allgemeinen zuerst die selektivere Spalte verwendet wird, ist es wahrscheinlicher, dass der Index verwendet wird.
Mitch Wheat

Antworten:


72

Überlegen Sie, was ein Index in SQL ist - und Index ist wirklich ein Speicherblock, der auf andere Speicherblöcke zeigt (dh Zeiger auf Zeilen). Der Index ist in Seiten unterteilt, sodass Teile des Index je nach Verwendung aus dem Speicher geladen und entladen werden können.

Wenn Sie nach einer Reihe von Zeilen fragen, verwendet SQL den Index, um die Zeilen schneller zu finden als das Scannen von Tabellen (wobei jede Zeile betrachtet wird).

SQL verfügt über geclusterte und nicht geclusterte Indizes. Mein Verständnis von Clustered-Indizes ist, dass sie ähnliche Indexwerte auf derselben Seite gruppieren. Auf diese Weise kann SQL diese Zeilen von einer gruppierten Speicherseite zurückgeben, wenn Sie nach allen Zeilen fragen, die einem Indexwert entsprechen. Aus diesem Grund ist der Versuch, eine GUID-Spalte zu gruppieren, eine schlechte Idee. Sie versuchen nicht, zufällige Werte zu gruppieren.

Wenn Sie eine Ganzzahlspalte indizieren, enthält der SQL-Index eine Reihe von Zeilen für jeden Indexwert. Wenn Sie einen Bereich von 1 bis 10 haben, haben Sie 10 Indexzeiger. Je nachdem, wie viele Zeilen es gibt, kann dies unterschiedlich ausgelagert werden. Wenn Ihre Abfrage nach dem Index sucht, der mit "1" übereinstimmt, und dann, wo Name "Fred" enthält (vorausgesetzt, die Spalte "Name" ist nicht indiziert), erhält SQL sehr schnell den Satz von Zeilen, die mit "1" übereinstimmen, und die Tabelle durchsucht den Rest.

Was SQL also wirklich tut, ist zu versuchen, die Arbeitsmenge (Anzahl der Zeilen) zu reduzieren, über die es iterieren muss.

Wenn Sie ein Bitfeld (oder einen engen Bereich) indizieren, reduzieren Sie den Arbeitssatz nur um die Anzahl der Zeilen, die diesem Wert entsprechen. Wenn Sie eine kleine Anzahl übereinstimmender Zeilen haben, würde dies Ihren Arbeitssatz erheblich reduzieren. Bei einer großen Anzahl von Zeilen mit einer 50/50-Verteilung kann dies zu einem sehr geringen Leistungsgewinn führen, während der Index auf dem neuesten Stand gehalten wird.

Der Grund, warum jeder sagt, dass er testen soll, ist, dass SQL einen sehr cleveren und komplexen Optimierer enthält, der einen Index möglicherweise ignoriert, wenn er entscheidet, dass das Scannen von Tabellen schneller ist, eine Sortierung verwendet oder Speicherseiten organisiert, wie es ihm gefällt.


Es klingt also so, als ob ich immer nur eine Handvoll Zeilen habe, in denen das Bitfeld 1 ist (zum Beispiel "IsProcessed" verfolgen), dann wäre ein Index gut, weil er sie nach Wert ordnet und dann die auswählen kann kleines Arbeitsset sehr schnell. Wenn Sie einverstanden sind, fügen Sie das hinzu und ich werde es akzeptieren.
Jeremcc

2
In meinem vorherigen Kommentar meine ich, dass diese Aussage: "Wenn Sie ein Bitfeld (oder einen engen Bereich) indizieren, reduzieren Sie den Arbeitssatz nur um die Hälfte" ist nicht wahr, wenn die Verteilung stark auf einen Wert gewichtet ist. Aber ich mag den Rest Ihrer Antwort. Wenn Sie das beheben, werde ich es akzeptieren.
Jeremcc

1
Getan. Ich dachte, dass für eine Million Zeilen ein Bitfeld eine 50% ige Verteilung haben würde, aber Sie haben Recht, dass es für einen bestimmten Problembereich den Arbeitssatz erheblich reduzieren könnte.
Geoff Cox

Es lohnt sich, Ausführungspläne mit und ohne Index zu betrachten und festzustellen, ob der Index verwendet wird und ob er tatsächlich die Kosten Ihrer Abfragen senkt. Einfach und wissenschaftlich!
Onupdatecascade

Was ist mit der Indizierung eines Bitfelds + eines anderen Feldes? Z.B. In einem Webaktivitätsprotokoll würde man den Zeitstempel indizieren, aber ein anderer nützlicher Index könnte sich in einem Bitfeld "IsHTTPS" + Zeitstempel befinden, um alle https-Aktionen schnell anzuzeigen. Wäre das auch ineffizient?
ingredient_15939

19

Ich bin gerade über eine andere auf diese Frage gestoßen. Angenommen, Ihre Aussage, dass nur eine Handvoll Datensätze den Wert 1 annehmen (und dass dies diejenigen sind, an denen Sie interessiert sind), könnte ein gefilterter Index eine gute Wahl sein. Etwas wie:

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

Dadurch wird ein wesentlich kleinerer Index erstellt, den der Optimierer verwenden kann, wenn dies ein Prädikat in Ihrer Abfrage ist.


1
Es ist zu beachten, dass das Prädikat in der Abfrage fest auf den Wert im gefilterten Index codiert werden muss. Wenn Sie den Wert in einem Parameter übergeben yourBitColumn = @value, kann der Optimierer nicht feststellen, ob der gefilterte Index verwendbar ist.
Geofftnz

2
Es gibt Möglichkeiten, dies zu umgehen, aber Sie haben Recht. Der Optimierer benötigt zum Zeitpunkt der Kompilierung eine Garantie dafür, dass die Werte für alle Prädikate, die mit dem gefilterten Indexprädikat übereinstimmen, statisch / invariant sind, da es die Aufgabe des Optimierers ist, einen allgemeinen Plan zu erstellen, der für jeden Parametersatz funktioniert .
Ben Thul

9

100 Millionen Datensätze, bei denen nur wenige das Bitfeld auf 1 setzen? Ja, ich würde denken, dass das Indizieren des Bitfelds das Abfragen der Bit = 1-Datensätze definitiv beschleunigen würde. Sie sollten die logarithmische Suchzeit aus dem Index abrufen und dann nur die wenigen Seiten mit Bit = 1-Datensätzen berühren. Andernfalls müssten Sie alle Seiten der 100-Millionen-Datensatztabelle berühren.

Andererseits bin ich definitiv kein Datenbankexperte und könnte etwas Wichtiges vermissen.


8

Wenn Ihre Verteilung ziemlich bekannt und unausgeglichen ist, wie 99% der Zeilen Bit = 1 und 1% Bit = 0, wird bei einer WHERE-Klausel mit Bit = 1 etwa zur gleichen Zeit ein vollständiger Tabellenscan durchgeführt wie der Index-Scan. Wenn Sie eine schnelle Abfrage mit Bit = 0 haben möchten, ist der beste mir bekannte Weg, einen gefilterten Index zu erstellen und eine Klausel WHERE bit = 0 hinzuzufügen. Auf diese Weise speichert dieser Index nur die 1% -Zeile. Wenn Sie dann ein WHERE-Bit = 0 ausführen, kann der Abfrageoptimierer einfach diesen Index auswählen, und alle Zeilen daraus sind Bit = 0. Sie haben auch den Vorteil, dass nur sehr wenig Speicherplatz erforderlich ist, um einen vollständigen Index für das Bit zu vergleichen .


2
Wenn 99% der Zeilen Bit = 1 sind, sollte der Optimierer den Index ignorieren und einen Tabellenscan durchführen. Die Verwendung des Index ist tatsächlich schlechter als ein Tabellenscan, zumindest auf einem Rotationslaufwerk, mehr E / A und nicht aufeinanderfolgende Lesevorgänge von der Festplatte. Der gefilterte Index (Postgres-Äquivalent: Teilindex) ist der richtige Weg. Ich denke, weil es Jahre nach der Frage ist, hat diese Antwort nicht die Stimmen bekommen, die sie verdient hat.
Andrew Lazarus

7

Obwohl ich nicht glaube, dass ich NUR eine Bitspalte selbst indizieren würde, ist es sehr üblich, Bitspalten als Teil eines zusammengesetzten Index einzuschließen.

Ein einfaches Beispiel wäre ein Index für ACTIVE, LASTNAME anstelle von nur Nachname, wenn Ihre Anwendung fast immer nach aktiven Kunden sucht.


7
In dem Beispiel, das Sie gegeben haben, wäre ich eher geneigt, den Nachnamen an die erste Stelle zu setzen. Dies hängt von der spezifischen Abfragearbeitslast ab. Wenn jedoch im Allgemeinen zuerst die selektivere Spalte verwendet wird, ist es wahrscheinlicher, dass der Index verwendet wird.
Mitch Wheat

7

Dieser Artikel ist nicht mehr sichtbar
Homer6

@ Homer6 Ich habe einen Link zu dem neuen Zuhause für diesen Artikel hinzugefügt.
Jeff

Neuer Link geht zur Toad World Homepage.
N West

Fand den Artikel mit der Wayback-Maschine und fand einen neuen verwandten Artikel. Hoffe das hilft.
Jeff

2

Natürlich lohnt es sich, besonders wenn Sie die Daten mit diesem Wert abrufen müssen. Es wäre ähnlich wie bei der Verwendung einer spärlichen Matrix anstelle einer normalen Matrix.

Mit SQL 2008 können Sie jetzt Partitionierungsfunktionen verwenden und die Daten filtern, die in einem Index enthalten sind. Der Nachteil früherer Versionen wäre, dass der Index für alle Daten erstellt wird. Dies kann jedoch optimiert werden, indem die interessanten Werte in einer separaten Dateigruppe gespeichert werden.


2

Wie andere gesagt haben, möchten Sie dies messen. Ich erinnere mich nicht, wo ich das gelesen habe, aber eine Spalte muss eine sehr hohe Kardinalität haben (ca. 95%), damit ein Index effektiv ist. Ihr bester Test hierfür wäre, den Index zu erstellen und die Ausführungspläne für die 0- und 1-Werte des BIT-Felds zu untersuchen. Wenn im Ausführungsplan eine Indexsuchoperation angezeigt wird, wissen Sie, dass Ihr Index verwendet wird.

Ihre beste Vorgehensweise wäre, die Tabelle SELECT * FROM mit einer einfachen Tabelle zu testen. WHERE BitField = 1; Fragen Sie die Funktionalität ab und bauen Sie sie langsam Schritt für Schritt aus, bis Sie eine realistische Abfrage für Ihre Anwendung haben. Überprüfen Sie den Ausführungsplan bei jedem Schritt, um sicherzustellen, dass die Indexsuche weiterhin verwendet wird. Zwar gibt es keine Garantie dafür, dass dieser Ausführungsplan in der Produktion verwendet wird, aber es besteht eine gute Chance, dass dies der Fall ist.

Einige Informationen finden Sie in den Foren von sql-server-performance.com und im Artikel, auf den verwiesen wird


Es ist nicht so sehr die Kardinalität der Kolumne als Ganzes, die zählt. Dies ist die Selektivität der WHERE-Klausel. Wenn es also nur wenige Spalten mit dem Wert 1 gibt, kann die Indizierung dennoch sinnvoll sein. Wenn es 50/50 ist (zB männlich / weiblich), dann ist es das nicht wert.
WW.

2

"Ich erinnere mich, dass ich an einer Stelle gelesen habe, dass es sich nicht wirklich lohnt, ein Feld mit geringer Kardinalität (einer geringen Anzahl unterschiedlicher Werte) zu indizieren."

Dies liegt daran, dass SQL Server es fast immer effizienter findet, nur einen Tabellenscan durchzuführen, als den Index zu lesen. Im Grunde genommen wird Ihr Index niemals verwendet und es ist eine Verschwendung, ihn zu pflegen. Wie andere gesagt haben, könnte es in einem zusammengesetzten Index in Ordnung sein.


2

Wenn Sie die Abfrage nach Datensätzen beschleunigen möchten, bei denen der Bitfeldwert gleich '1' ist, können Sie eine indizierte Ansicht Ihrer Basistabelle versuchen, die nur Datensätze enthält, bei denen Ihr Bitfeld gleich '1' ist. Wenn in einer Enterprise Edition eine Abfrage eine indizierte Ansicht anstelle einer angegebenen Tabelle verwenden könnte, um die Abfrageleistung zu verbessern, wird die Ansicht verwendet. Theoretisch würde dies die Geschwindigkeit ausgewählter Abfragen erhöhen, die nur nach Datensätzen mit einem Bitfeldwert von '1' suchen.

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

All dies setzt voraus, dass Sie Microsoft SQL Server 2005 Enterprise sind. Das gleiche könnte für 2008 gelten, ich bin mit dieser Version nicht vertraut.


2

Wenn Sie wissen möchten, ob ein Index die gewünschten Auswirkungen hat: Testen Sie und testen Sie erneut.

Im Allgemeinen möchten Sie keinen Index, der Ihre Tabelle aufgrund der Kosten für die Verwaltung eines Index nicht ausreichend einschränkt. (Kosten> Gewinn). Aber wenn der Index in Ihrem Fall die Tabelle halbiert, können Sie etwas gewinnen, aber es auf den Tisch legen. Es hängt alles von der genauen Größe / Struktur Ihrer Tabelle ab und davon, wie Sie sie verwenden (Anzahl der Lese- / Schreibvorgänge).


1

Nein, da dies zu einer sehr geringen Selektivität führt. Als Teil eines zusammengesetzten Index. durchaus aber erst nach anderen gleichheitsspalten.


1

Sie können in SQL Server 2000 kein Bitfeld indizieren, wie dies zu diesem Zeitpunkt in den Online-Büchern angegeben war:

bisschen

Ganzzahliger Datentyp 1, 0 oder NULL.

Bemerkungen

Spalten vom Typ Bit können keine Indizes enthalten.

Ja, wenn Sie nur eine Handvoll von Millionen Zeilen haben, hilft ein Index. Wenn Sie dies in diesem Fall tun möchten, müssen Sie die Spalte a erstellen tinyint.

Hinweis : Mit Enterprise Manager können Sie keinen Index für eine Bitspalte erstellen. Wenn Sie möchten, können Sie dennoch manuell einen Index für eine Bitspalte erstellen:

CREATE INDEX IX_Users_IsActiveUsername ON Users
(
   IsActive,
   Username
)

SQL Server 2000 verwendet jedoch keinen solchen Index. Es wird eine Abfrage ausgeführt, bei der der Index ein perfekter Kandidat wäre, z.

SELECT TOP 1 Username 
FROM Users
WHERE IsActive = 0

SQL Server 2000 führt stattdessen einen Tabellenscan durch, als ob der Index nicht einmal vorhanden wäre. Wenn Sie die Spalte mit einer Tinyint SQL Server 2000 ändern wird tun versuchen , einen Index. Außerdem die folgende nicht abgedeckte Abfrage:

SELECT TOP 1 * 
FROM Users
WHERE IsActive = 0

Es wird eine Indexsuche durchgeführt, gefolgt von einer Lesezeichensuche.


SQL Server 2005 unterstützt Indizes für Bitspalten nur eingeschränkt. Beispielsweise:

SELECT TOP 1 Username 
FROM Users
WHERE IsActive = 0

bewirkt eine Indexsuche durch den Deckungsindex. Aber der nicht abgedeckte Fall:

SELECT TOP 1 * 
FROM Users
WHERE IsActive = 0

führt nicht zu einer Indexsuche, gefolgt von einer Lesezeichensuche, sondern führt einen Tabellenscan (oder einen Clustered-Index-Scan) durch, anstatt die Indexsuche gefolgt von einer Lesezeichensuche durchzuführen.

Verifiziert durch Experimente und direkte Beobachtung.


Zu Ihrer Information: Mit SQL Server 2005 Management Studio können Sie dies tun.
Jeremcc

Mit meiner Kopie von SQL Server 2000 konnte ich einen Index für eine Bitspalte festlegen.
Kibbee

Mit meiner Kopie von SQL Server 2000 kann ich keinen Index für eine Bitspalte festlegen.
Ian Boyd

1

sehr späte Antwort ...

Ja, es kann laut SQL CAT-Team nützlich sein (aktualisiert, konsolidiert)


1
Der Link scheint jetzt tot zu sein. Dieser Beitrag scheint jedoch zusammen mit mehreren anderen in einem E-Book konsolidiert worden zu sein . Der Abschnitt, auf den verwiesen wird, beginnt auf Seite 86. Das E-Book kann von SQLCAT.com eBooks unter dem Link "SQLCATs Handbuch zur relationalen Engine" heruntergeladen werden .
mwolfe02

0

Ist das eine häufige Abfrage? Es mag sich lohnen, nach einer "Handvoll" Datensätzen zu suchen, aber in den anderen Zeilen hilft es Ihnen nicht viel. Gibt es andere Möglichkeiten, die Daten zu identifizieren?


0

Kardinalität ist ein Faktor, der andere ist, wie gut der Index Ihre Daten aufteilt. Wenn Sie ungefähr eine halbe und eine halbe Null haben, hilft es. (Angenommen, dieser Index ist ein besserer Weg als ein anderer Index). Wie oft fügen Sie jedoch ein und aktualisieren es? Das Hinzufügen von Indizes für die SELECT-Leistung beeinträchtigt auch die INSERT-, UPDATE- und DELETE-Leistung. Denken Sie also daran.

Ich würde sagen, wenn die Einsen bis Nullen (oder umgekehrt) nicht besser als 75% bis 25% sind, stören Sie sich nicht.


1
Ich würde nicht zustimmen. Wenn Ihre Verteilung 50/50 ist, würden Sie den Index niemals verwenden, da es nur schneller wäre, einen Tabellenscan durchzuführen. Wenn Sie jedoch nur 5, 1 Werte und 1 Million 0 Werte haben, ist es sehr wahrscheinlich, dass der Index bei der Suche nach 1 verwendet wird.
Kibbee

0

Messen Sie die Reaktionszeit vorher und nachher und prüfen Sie, ob es sich lohnt. Theoretisch sollte es die Leistung für Abfragen verbessern, die die indizierten Felder verwenden, aber es hängt wirklich von der Verteilung der wahren / falschen Werte und den anderen Feldern ab, die an den Abfragen beteiligt sind, um die Sie sich kümmern


0

Ian Boyd hat Recht, wenn er sagt, dass Sie dies nicht über Enterprise Manager für SQL 2000 tun können (siehe seinen Hinweis zum Erstellen über T-SQL).


0

Sie müssen hier klug sein, um abzufragen. Sie müssen den Ladewert in Ihrer Spalte kennen, wenn die Last von true in Ihrem System höher ist, und Sie möchten alle wahren Werte überprüfen. Schreiben Sie Ihre Abfrage, um nicht false zu überprüfen. Dies hilft sehr , es ist nur ein Trick.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.