Ich habe eine SQL Server-Tabelle mit über 3 Milliarden Zeilen. Eine meiner Abfragen dauert sehr lange, daher überlege ich, sie zu optimieren. Die Abfrage sieht folgendermaßen aus:
SELECT [Enroll_Date]
,Count(*) AS [Record #]
,Count(Distinct UserID) AS [User #]
FROM UserTable
GROUP BY [Enroll_Date]
Das [Enroll_Date] ist eine Spalte mit niedriger Selektivität mit weniger als 50 möglichen Werten, während die Spalte UserID eine Spalte mit hoher Selektivität mit mehr als 200 Millionen unterschiedlichen Werten ist. Aufgrund meiner Forschung glaube ich, dass ich für diese beiden Spalten einen nicht gruppierten zusammengesetzten Index erstellen sollte, und theoretisch sollte die Spalte mit hoher Selektivität die erste Spalte sein. Aber ich bin mir in meinem Fall nicht sicher, ob das funktionieren würde, da ich die Spalte mit niedriger Selektivität in der group by-Klausel verwende.
Diese Tabelle hat keinen Clustered-Index.
quelle
Antworten:
Als Alternative zur Lösung von @ AaronBertrand (wenn Sie keine indizierte Ansicht erstellen können oder möchten) würde ich Ihnen empfehlen, einen Index für zu erstellen
(Enroll_Date, UserID)
. Wenn diese Art von Frage in Ihrer Tabelle sehr häufig vorkommt, sollte dies wahrscheinlich sogar Ihr Clustered-Index sein.Ich würde im Allgemeinen keine Indizes mit hoher Selektivität als allgemeine "Best Practice" empfehlen, sondern vielmehr prüfen, welcher Index Ihrer Abfrage die beste Leistung verleiht.
Ein Index für
(Enroll_Date, UserID)
gibt Ihrer Abfrage einen hochoptimierten, nicht blockierenden Abfrageplan mit Stream-Aggregaten."Nicht blockierend" bedeutet in diesem Zusammenhang, dass die Abfrage keine signifikanten Datenmengen puffern muss (wie zum Beispiel eine Sortierung oder ein Hash-Aggregat), was bedeutet, dass (a) sofort Zeilen zurückgegeben werden und b) verbraucht praktisch keinen Arbeitsspeicher.
quelle
Aaron Antwort ist eine großartige Lösung. Ich werde die Frage beantworten, vorausgesetzt, Sie möchten diesen Ansatz nicht wählen.
Die Abfrage, die Sie gepostet haben, wird normalerweise ausgeführt, indem Sie zuerst gruppieren
(Enroll_Date, UserID)
und dann wieder ein(Enroll_Date)
. Diese Optimierung ist neu in SQL Server 2012. Sie wird im Falle einer einzelnen wirksamCOUNT DISTINCT
.Ein Index für diese beiden Spalten in der angegebenen Reihenfolge reicht
(Enroll_Date, UserID)
aus, um einen effizienten Plan zu erhalten, der einen Index-Scan in zwei aufeinanderfolgende Stream-Aggregate unterteilt. Die entgegengesetzte Reihenfolge würde diesen Plan nicht ermöglichen.Verwenden Sie daher die Reihenfolge
(Enroll_Date, UserID)
. Sie haben hier keine Wahl.quelle
Klingt nach einem idealen Szenario für eine indizierte Ansicht, mit der Sie Berechnungen und Aggregate zum Zeitpunkt des Schreibens anstelle der Abfragezeit bezahlen können.
Das Erstellen dauert einige Zeit und erfordert natürlich eine Wartung aller DML-Vorgänge, genau wie ein Index für die Basistabelle.
Jetzt wäre die Abfrage für diese Ansicht ziemlich ähnlich - jede Zeile in der Ansicht stellt jetzt eine eigene Benutzer- / Datumskombination dar, sodass diese Zahl durch einen einzelnen COUNT (*) berechnet werden kann, während die Gesamtzahl der Zeilen in der Basistabelle gleich ist bereits teilweise für Sie aggregiert, jetzt müssen Sie sie nur noch mit SUM pro Datum addieren:
NOEXPAND-Hinweis hinzugefügt, nachdem dies und das gespeichert wurde .
Ich kann Ihnen ohne Zweifel sagen, dass diese Abfrage schneller ist als Ihre aktuelle Abfrage (aber nicht um wie viel), außer in dem seltenen Fall, dass Sie genau einen Benutzer für jedes Datum haben (in diesem Fall wird dieselbe Datenmenge vorhanden sein) zu lesen) und die uns bekannten Spalten sind die einzigen Spalten im Index der Basistabelle. Ob diese Leistungssteigerung zum Zeitpunkt des Lesens die zusätzliche Arbeit wert ist, die sich auf den Schreibanteil Ihrer Arbeitslast auswirkt, können wir Ihnen nicht sagen - Sie müssen sie testen, um den Kompromiss zu messen (kein Index ist frei).
Und wenn Sie häufig dieselben allgemeinen WHERE-Klauseln für Enroll_Date für bestimmte, genau definierte Bereiche verwenden (z. B. das aktuelle Quartal oder Jahr bis heute), können Sie übereinstimmende gefilterte Indizes hinzufügen, die diese E / A noch weiter reduzieren (aber es gibt immer eine Abtausch).
Sie können auch einen Clustered-Index für die Basistabelle erstellen. Dies scheint nicht einer der sehr seltenen Anwendungsfälle zu sein, die von einem Haufen profitieren.
quelle