Frage

Wenn Sie durch Daten Paging, die von einer DB kommt, müssen Sie wissen, wie viele Seiten es wird die Seitenkontrollen zu machen.

Zur Zeit ich, dass die Abfrage zweimal ausgeführt wird, einmal in einem count() wickelte die Gesamtergebnisse, und ein zweites Mal mit einer Grenze zu bestimmen, angewandt, nur um wieder die Ergebnisse, die ich für die aktuelle Seite benötigen.

Dies scheint ineffizient. Gibt es eine bessere Art und Weise zu bestimmen, wie viele Ergebnisse zurückgegeben worden wäre, bevor LIMIT angewandt wurde?

ich PHP und Postgres verwenden.

War es hilfreich?

Lösung

Reine SQL

Die Dinge haben sich seit 2008 geändert Sie ein Fensterfunktion verwenden können, die volle Zahl und die begrenzte Folge in einer Abfrage zu erhalten. (Eingeführt mit PostgreSQL 8.4 im Jahr 2009 ).

SELECT foo
     , count(*) OVER() AS full_count
FROM   bar
WHERE  <some condition>
ORDER  BY <some col>
LIMIT  <pagesize>
OFFSET <offset>

Beachten Sie, dass diese als wesentlich teurer sein kann, ohne die Gesamtzahl. Alle Zeilen gezählt werden, und eine mögliche Verknüpfung nur die oberen Zeilen aus einem passenden Index einnehmen, kann nicht hilfreich sein, nicht mehr.
Ist nicht viel mit kleinen Tischen oder full_count <= OFFSET + LIMIT. Angelegenheiten für eine wesentlich größere full_count.

Corner Fall : Wenn OFFSET ist mindestens so groß wie die Anzahl der Zeilen aus der Basisabfrage, keine Zeile zurückgegeben. So erhalten Sie auch keine full_count. Mögliche Alternative:

Betrachten Sie die Abfolge von Ereignissen :

  1. WHERE Klausel (und JOIN Bedingungen, aber nicht hier) -Filter qualifizierenden Zeilen aus der Basistabelle (n).

    (GROUP BY und Aggregatfunktionen würden hier.)

  2. Fensterfunktionen angewandt werden, unter Berücksichtigung aller qualifizierenden Zeilen (abhängig von der OVER Klausel und der Rahmenspezifikation der Funktion). Die einfache count(*) OVER() basiert auf allen Zeilen.

  3. ORDER BY

    (DISTINCT oder DISTINCT ON würde hier.)

  4. LIMIT / OFFSET angewendet werden auf der Grundlage der bestehenden Ordnung Zeilen auszuwählen zurückzukehren.

LIMIT / OFFSET wird mit einer wachsenden Anzahl von Zeilen in der Tabelle zunehmend ineffizient. Betrachten wir alternative Ansätze, wenn Sie eine bessere Leistung benötigen:

Alternativen Endzahl zu bekommen

Es gibt völlig unterschiedliche Ansätze die Anzahl der betroffenen Zeilen ( nicht die volle Zählung vor OFFSET & LIMIT angewendet wurden) zu erhalten. Postgres hat interne Buchhaltung, wie viele Zeilen, in denen durch den letzten SQL-Befehl betroffen. Einige Kunden können auf diese Informationen zugreifen oder Zeilen selbst zählen (wie psql).

Zum Beispiel können Sie die Anzahl der betroffenen Zeilen in abrufen plpgsql unmittelbar nach einem SQL-Befehl ausführen mit:

GET DIAGNOSTICS integer_var = ROW_COUNT;

Einzelheiten im Handbuch.

Oder Sie verwenden pg_num_rows in PHP . Oder ähnliche Funktionen in anderen Clients.

Siehe auch:

Andere Tipps

Wie beschreibe ich auf meinem Blog , MySQL verfügt über eine Funktion SQL_CALC_FOUND_ROWS genannt. Dadurch entfällt die Notwendigkeit, die Abfrage zweimal zu tun, aber es muss noch die Abfrage in seiner entireity tun, auch wenn die Grenze Klausel erlaubt hätte es früh zu beenden.

Soweit ich weiß, gibt es keine ähnliche Funktion für PostgreSQL. Eine Sache zu beachten, wenn Paginierung tut (die häufigste Sache, für das LIMIT IMHO verwendet wird): tut eine „OFFSET 1000 LIMIT 10“ bedeutet, dass die DB zu holen hat mindestens 1010 Zeilen, auch wenn es gibt Ihnen nur 10. Eine leistungsfähigere Art und Weise zu tun, ist es, den Wert der Zeile erinnern Sie sich für die vorhergehende Zeile (1000. in diesem Fall) bestellen durch und schreiben Sie die Abfrage wie folgt:“... WHERE order_row> value_of_1000_th LIMIT 10" . Der Vorteil ist, dass „order_row“ wird höchstwahrscheinlich indiziert (wenn nicht, haben Sie ein Problem gehen). Der Nachteil ist, dass, wenn neue Elemente zwischen Seitenaufrufen hinzugefügt werden, diese ein wenig aus dem Takt zu bekommen (aber dann wieder, es kann nicht von den Besuchern zu beobachten sein und können eine große Leistungssteigerung sein).

Sie können die Leistung Strafe mildern, indem nicht jedes Mal die COUNT () Abfrage ausgeführt wird. Cache, um die Anzahl der Seiten für, sagen wir 5 Minuten, bevor die Abfrage erneut ausgeführt wird. Es sei denn, Sie sind eine große Anzahl von Einfügungen zu sehen, dass sollte gut funktionieren.

Da Postgres bereits eine bestimmte Menge an Caching Dinge tut, ist diese Art der Methode nicht so ineffizient wie es scheint. Es ist definitiv nicht die Ausführungszeit zu verdoppeln. Wir haben Timer in unsere DB-Schicht aufgebaut, so habe ich die Beweise gesehen.

Sehen, wie Sie zum Zweck des Paging wissen müssen, würde ich vorschlagen, sobald die vollständige Abfrage ausgeführt wird, das Schreiben der Daten auf der Festplatte als serverseitige Cache, dann füttern, dass durch Ihren Paging-Mechanismus.

Wenn Sie die COUNT-Abfrage für den Zweck laufen zu entscheiden, ob die Daten für den Benutzer zur Verfügung zu stellen oder nicht (dh, wenn es> X Aufzeichnungen, gibt einen Fehler zurück), müssen Sie mit dem COUNT Ansatz bleiben.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top