Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

SQL-Leistung:WHERE vs. WHERE(ROW_NUMBER)

Wie bereits erwähnt, geben die Abfragen unterschiedliche Ergebnisse zurück und vergleichen Äpfel mit Birnen.

Aber die grundlegende Frage bleibt:was ist schneller:Keyset-gesteuertes Paging oder Rownumber-gesteuertes Paging?

Keyset-Paging

Keyset-gesteuertes Paging beruht darauf, sich die oberen und unteren Tasten der zuletzt angezeigten Seite zu merken und den nächsten oder vorherigen Satz von Zeilen basierend auf dem oberen/letzten Keyset anzufordern:

Nächste Seite:

select top (<pagesize>) ...
from <table>
where key > @last_key_on_current_page
order by key;

Vorherige Seite:

select top (<pagesize>)
from <table>
where key < @first_key_on_current_page
order by key desc;

Dieser Ansatz hat zwei Hauptvorteile gegenüber dem ROW_NUMBER-Ansatz oder dem äquivalenten LIMIT-Ansatz von MySQL:

  • ist richtig :Im Gegensatz zum zeilennummerbasierten Ansatz werden neue Einträge und gelöschte Einträge korrekt behandelt. Die letzte Zeile von Seite 4 wird nicht als erste Zeile von Seite 5 angezeigt, nur weil Zeile 23 auf Seite 2 zwischenzeitlich gelöscht wurde. Auch verschwinden Zeilen nicht auf mysteriöse Weise zwischen den Seiten. Diese Anomalien sind bei dem auf Zeilennummern basierenden Ansatz üblich, aber die auf Schlüsselsätzen basierende Lösung vermeidet sie viel besser.
  • ist schnell :Alle Operationen können mit einer schnellen Reihenpositionierung gefolgt von einem Entfernungsscan in die gewünschte Richtung gelöst werden

Dieser Ansatz ist jedoch schwierig zu implementieren, für den durchschnittlichen Programmierer schwer verständlich und von den Tools nicht unterstützt.

Zeilennummer gesteuert

Dies ist der allgemeine Ansatz, der mit Linq-Abfragen eingeführt wurde:

select ...
from (
  select ..., row_number() over (...) as rn
  from table)
where rn between @firstRow and @lastRow;

(oder eine ähnliche Abfrage mit TOP) Dieser Ansatz ist einfach zu implementieren und wird von Tools unterstützt (insbesondere von Linq .Limit- und .Take-Operatoren). Aber dieser Ansatz ist garantiert um den Index zu scannen, um die Zeilen zu zählen. Dieser Ansatz funktioniert normalerweise sehr schnell für Seite 1 und verlangsamt sich allmählich, wenn die eine zu immer höheren Seitenzahlen geht.

Als Bonus ist es mit dieser Lösung sehr einfach, die Sortierreihenfolge zu ändern (ändern Sie einfach die OVER-Klausel).

Insgesamt angesichts der Einfachheit der auf ROW_NUMBER() basierenden Lösungen, der Unterstützung, die sie von Linq haben, der Einfachheit, beliebige Reihenfolgen für moderate Datensätze zu verwenden die auf ROW_NUMBER basierenden Lösungen sind angemessen. Bei großen und sehr großen Datensätzen kann die ROW_NUMBER() zu schwerwiegenden Leistungsproblemen führen.

Eine andere zu berücksichtigende Sache ist, dass es oft ein bestimmtes Zugriffsmuster gibt. Oft sind die ersten paar Seiten heiß und Seiten nach 10 werden im Grunde nie angesehen (z. B. die neuesten Beiträge). In diesem Fall kann die Strafe, die bei ROW_NUMBER() für den Besuch von Unterseiten auftritt (Anzeigeseiten, für die eine große Anzahl von Zeilen gezählt werden muss, um die Startergebniszeile zu erhalten), gut ignoriert werden.

Und schließlich ist die Keyset-Paginierung großartig für die Wörterbuchnavigation, die ROW_NUMBER() nicht ohne weiteres aufnehmen kann. Bei der Wörterbuchnavigation können Benutzer anstelle der Seitenzahl zu bestimmten Ankern wie Buchstaben navigieren. Typisches Beispiel ist eine Kontakt-Rolodex-ähnliche Seitenleiste, Sie klicken auf M und navigieren zum ersten Kundennamen, der mit M beginnt.