Lagerreihenfolge vs Ergebnisreihenfolge

8

Dies ist eine Ausgründungsfrage aus der im Primärschlüssel angegebenen Sortierreihenfolge, die Sortierung wird jedoch bei SELECT ausgeführt .

@Catcall sagt dies zum Thema Speicherreihenfolge (Clustered Index) und Ausgabereihenfolge

Viele Leute glauben, dass ein Clustered-Index eine Sortierreihenfolge bei der Ausgabe garantiert. Aber das ist es nicht; Es garantiert eine Speicherreihenfolge auf der Festplatte. Siehe zum Beispiel diesen Blog-Beitrag .

Ich habe den Blog-Beitrag von Hugo Kornelis gelesen und verstehe, dass ein Index nicht garantiert, dass der SQL-Server die Datensätze in einer bestimmten Reihenfolge liest. Es fällt mir jedoch schwer zu akzeptieren, dass ich dies für mein Szenario nicht annehmen kann?

CREATE TABLE [dbo].[SensorValues](
  [DeviceId] [int] NOT NULL,
  [SensorId] [int] NOT NULL,
  [SensorValue] [int] NOT NULL,
  [Date] [int] NOT NULL,
CONSTRAINT [PK_SensorValues] PRIMARY KEY CLUSTERED 
(
  [DeviceId] ASC,
  [SensorId] ASC,
  [Date] DESC
) WITH (
    FILLFACTOR=75,
    DATA_COMPRESSION = PAGE,
    PAD_INDEX = OFF,
    STATISTICS_NORECOMPUTE = OFF,
    SORT_IN_TEMPDB = OFF,
    IGNORE_DUP_KEY = OFF,
    ONLINE = OFF,
    ALLOW_ROW_LOCKS = ON,
    ALLOW_PAGE_LOCKS = ON)
  ON [MyPartitioningScheme]([Date])

Meine ursprüngliche Anfrage war folgende:

SELECT TOP 1 SensorValue
  FROM SensorValues
  WHERE SensorId = 53
    AND DeviceId = 3819
    AND Date < 1339225010
  ORDER BY Date DESC

Aber ich schlage vor, dass ich auch dieses verwenden könnte (lesen Sie unten für meine Erklärung):

SELECT TOP 1 SensorValue
  FROM SensorValues
  WHERE SensorId = 53
    AND DeviceId = 3819
    AND Date < 1339225010

Wie Sie sehen können, sind meine Tabellenzeilen klein (16 Byte) und ich habe nur einen Index, einen Cluster. In meinem Szenario besteht die Tabelle derzeit aus 100.000.000 Datensätzen (und dies wird sich höchstwahrscheinlich verzehnfachen).

Wenn der Datenbankserver diese Tabelle abfragt, hat er zwei Möglichkeiten, meine Zeilen zu finden. Entweder sucht er nach dem Primärschlüssel und liest und gibt meine Werte in desc zurück. Reihenfolge des Datums, oder es muss ein vollständiger Tabellenscan durchgeführt werden. Mein Fazit ist, dass ein vollständiger Tabellenscan für all diese Datensätze viel zu langsam ist und der Datenbankserver daher die Tabelle immer über seinen Primärschlüssel sucht und dabei die nach sortierten Werte zurückgibtDate DESC

m__
quelle
2
Warum wollen Sie sich so sehr auf diese Annahme verlassen können? Warum ziehst du nicht einfach eine ORDER BYan, dann weißt du , dass du dich darauf verlassen kannst. Siehe # 3 hier
Aaron Bertrand
Aus zwei Gründen, Neugierde und weil die ORDER BYKlausel für mich ein großer Leistungshit ist (lesen Sie die andere Frage für weitere Informationen). Ich habe eine Lösung, die im Moment funktioniert, die jedoch nicht gilt, wenn und wenn mein Datenverkehr zunimmt.
m__
1
ORDER BY sollte kein Performance-Hit sein, wenn Sie sich auf die Reihenfolge verlassen, die Sie ohne die Bestellung von sehen - das ergibt für mich keinen Sinn.
Aaron Bertrand
4
Das einzige , was die Garantien festgelegte Reihenfolge Ergebnis ist eine ORDER BYKlausel in der Abfrage. Dies gilt für SQL Server , Oracle , MySQL und alle anderen denkbaren RDBMS. Versuchen Sie etwas anderes und Sie bereiten sich auf eine Überraschungsschale FAIL vor.
Nick Chammas

Antworten:

15

Lassen Sie mich versuchen zu erklären, warum Sie dies nicht tun sollten , warum Sie niemals davon ausgehen sollten , dass ein SQL-Produkt eine Ergebnismenge in einer bestimmten Reihenfolge zurückgibt, es sei denn, Sie geben dies an, unabhängig davon, welche Indizes - gruppiert oder nicht geclustert, B-Bäume oder R-Bäume oder kd-Bäume oder Fraktalbäume oder andere exotische Indizes, die ein DBMS verwendet.


Ihre ursprüngliche Abfrage weist das DBMS an, die SensorValuesTabelle zu durchsuchen , Zeilen zu finden, die den drei Bedingungen entsprechen, diese Zeilen nach Dateabsteigend zu ordnen , nur die erste Zeile von diesen zu behalten und schließlich nur die SensorValueSpalte auszuwählen und zurückzugeben .

SELECT TOP 1 SensorValue
  FROM SensorValues
  WHERE SensorId = 53
    AND DeviceId = 3819
    AND Date < 1339225010
  ORDER BY Date DESC ;

Dies sind sehr spezifische Befehle, die Sie dem DBMS erteilt haben, und das Ergebnis wird höchstwahrscheinlich jedes Mal das gleiche sein, wenn Sie die Abfrage ausführen (es besteht die Möglichkeit, dass dies nicht der Fall ist, wenn Sie mehr als eine Zeile haben, die den Bedingungen entspricht und dasselbe hat max Dateaber anders, SensorValueaber nehmen wir für den Rest der Konversation an, dass in Ihrer Tabelle keine solchen Zeilen vorhanden sind).

Muss das DBMS dies tun, um diese Abfrage genau so auszuführen, wie ich es oben beschrieben habe? Nein, natürlich nicht und das weißt du. Möglicherweise wird die Tabelle nicht gelesen, sondern aus einem Index gelesen. Oder es werden zwei Indizes verwendet, wenn es der Meinung ist, dass es besser (schneller) ist. Oder drei. Oder es wird ein zwischengespeichertes Ergebnis verwendet (nicht SQL Server, sondern andere Ergebnisse der DBMS-Cache-Abfrage). Oder es wird eine parallele Ausführung einmal verwendet und nicht das nächste Mal, wenn es ausgeführt wird. Oder ... (fügen Sie eine andere Funktion hinzu, die sich auf die Ausführung und die Ausführungspläne auswirkt).

Es wird jedoch garantiert, dass bei jeder Ausführung genau das gleiche Ergebnis zurückgegeben wird - solange keine Zeilen eingefügt, gelöscht oder aktualisiert werden.


Nun wollen wir sehen, was Ihr Vorschlag sagt:

SELECT TOP 1 SensorValue
  FROM SensorValues
  WHERE SensorId = 53
    AND DeviceId = 3819
    AND Date < 1339225010 ;

Diese Abfrage weist das DBMS an, die SensorValuesTabelle zu durchsuchen , Zeilen zu finden, die den drei Bedingungen entsprechen, diese Zeilen nach Dateabsteigend zu sortieren , sich nicht um die Reihenfolge zu kümmern, nur eine Zeile beizubehalten und schließlich nur die SensorValueSpalte auszuwählen und zurückzugeben .

Es sagt also im Grunde dasselbe wie das erste, außer dass es sagt, dass Sie nur ein Ergebnis wollen, das den Bedingungen entspricht, und es ist Ihnen egal, welches .

Können wir nun davon ausgehen, dass aufgrund des Clustered-Index immer das gleiche Ergebnis erzielt wird?
- Wenn dieser Clustered-Index jedes Mal verwendet wird, ja.

Aber wird es es benutzen?
- Nein.

Warum nicht?
- Weil es kann. Das Abfrageoptimierungsprogramm kann bei jeder Ausführung einer Anweisung einen Ausführungspfad auswählen. Welchen Weg es zu diesem Zeitpunkt für diese Aussage für richtig hält.

Aber ist die Verwendung des Clustered-Index nicht der beste / schnellste Weg, um Ergebnisse zu erzielen?
- Nein nicht immer. Es ist möglicherweise das erste Mal, dass Sie die Abfrage ausführen. Beim zweiten Mal wird möglicherweise ein zwischengespeichertes Ergebnis verwendet (wenn das DBMS über eine solche Funktion verfügt, nicht SQL Server * ). Beim 1000. Mal wurde das Ergebnis möglicherweise aus dem Cache entfernt, und dort ist möglicherweise ein anderes Ergebnis vorhanden. Angenommen, Sie haben diese Abfrage kurz zuvor ausgeführt:

SELECT TOP 1 SensorValue
  FROM SensorValues
  WHERE SensorId = 53
    AND DeviceId = 3819
    AND Date < 1339225010
  ORDER BY Date ASC ;         --- Notice the `ASC` here

und das zwischengespeicherte Ergebnis (aus der obigen Abfrage) ist ein anderes, anderes, das immer noch Ihren Bedingungen entspricht, aber nicht das erste in Ihrer (gewünschten) Bestellung ist. Und Sie haben dem DBMS gesagt, dass es sich nicht um die Bestellung kümmern soll.

OK, also kann nur der Cache dies beeinflussen?
- Nein, viele andere Dinge auch.

  • Andere Indizes wurden zu diesem Zeitpunkt vom DBMS als besser für diese Abfrage angesehen.
  • Ein Entwickler hat diesen Clustered-Index, den Sie hatten, geändert oder vollständig entfernt.
  • Sie oder ein anderer Entwickler haben einen weiteren Index hinzugefügt, den der Optimierer als effizienter als das CI eingestuft hat.
  • Sie haben auf eine neue Version aktualisiert und das neue Optimierungsprogramm weist einen kleinen Fehler oder eine Änderung in der Rangfolge und Auswahl der Ausführungspläne auf.
  • Statistiken wurden aktualisiert.
  • Stattdessen wurde die parallele Ausführung gewählt.

*: SQL Server speichert keine Abfrageergebnisse zwischen, aber die Enterprise Edition verfügt über eine erweiterte Scanfunktion, die insofern ähnlich ist, als Sie aufgrund gleichzeitiger Abfragen möglicherweise unterschiedliche Ergebnisse erhalten. Ich bin mir nicht sicher, wann genau dies einsetzt. (Danke @Martin Smith für den Tipp.)


Ich hoffe, Sie sind überzeugt, dass Sie sich niemals darauf verlassen sollten, dass eine SQL-Abfrage Ergebnisse in einer bestimmten Reihenfolge zurückgibt, es sei denn, Sie geben dies an. Und niemals TOP (n)ohne verwenden ORDER BY, es sei denn, Sie möchten natürlich nur n Zeilen im Ergebnis und es ist Ihnen egal, welche zurückgegeben werden.

ypercubeᵀᴹ
quelle
2
SQL Server Enterprise Edition verfügt über eine erweiterte Scanfunktion, die insofern ähnlich ist, als Sie aufgrund gleichzeitiger Abfragen möglicherweise unterschiedliche Ergebnisse erhalten. Ich bin mir nicht sicher, wann genau dies einsetzt.
Martin Smith
1
Eine andere Sache, die möglicherweise die Reihenfolge der Ergebnismengen "randomisiert" (selbst wenn die Abfrage anscheinend von einem geordneten Index gesteuert wird), ist die Parallelität. Ich habe gesehen, dass eine App, auf der fehlerhaftes SQL ausgeführt wurde, sich nach dem Aktivieren der automatischen Parallelität schlecht verhält (nicht SQL Server, aber ich denke, das könnte auch dort zutreffen).
Mat