Für Paginierungszwecke muss eine Abfrage mit den Klauseln LIMIT
und ausgeführt werden OFFSET
. Ich benötige aber auch die Anzahl der Zeilen, die von dieser Abfrage ohne die Klauseln LIMIT
und zurückgegeben werden OFFSET
.
Ich möchte rennen:
SELECT * FROM table WHERE /* whatever */ ORDER BY col1 LIMIT ? OFFSET ?
Und:
SELECT COUNT(*) FROM table WHERE /* whatever */
Gleichzeitig. Gibt es eine Möglichkeit, dies zu tun, insbesondere eine Möglichkeit, mit der Postgres es optimieren kann, sodass es schneller ist, als beide einzeln auszuführen?
Antworten:
Ja. Mit einer einfachen Fensterfunktion:
SELECT *, count(*) OVER() AS full_count FROM tbl WHERE /* whatever */ ORDER BY col1 OFFSET ? LIMIT ?
Beachten Sie, dass die Kosten wesentlich höher sind als ohne die Gesamtzahl, aber in der Regel immer noch günstiger als zwei separate Abfragen. Postgres muss tatsächlich alle Zeilen in beide Richtungen zählen, was abhängig von der Gesamtzahl der qualifizierenden Zeilen Kosten verursacht. Einzelheiten:
Jedoch , wie Dani wies darauf hin , wenn
OFFSET
mindestens so groß wie die Anzahl der Zeilen aus der Basis Abfrage zurückgegeben, werden keine Zeilen zurückgegeben. Also bekommen wir auch nichtfull_count
.Wenn dies nicht akzeptabel ist, besteht eine mögliche Problemumgehung, um immer die volle Anzahl zurückzugeben , in einem CTE und einem
OUTER JOIN
:WITH cte AS ( SELECT * FROM tbl WHERE /* whatever */ ) SELECT * FROM ( TABLE cte ORDER BY col1 LIMIT ? OFFSET ? ) sub RIGHT JOIN (SELECT count(*) FROM cte) c(full_count) ON true;
Sie erhalten eine Zeile mit NULL-Werten mit dem
full_count
angehängten Wert, wenn dieserOFFSET
zu groß ist. Andernfalls wird es wie in der ersten Abfrage an jede Zeile angehängt.Wenn eine Zeile mit allen NULL-Werten ein mögliches gültiges Ergebnis ist, müssen Sie überprüfen
offset >= full_count
, ob der Ursprung der leeren Zeile eindeutig ist.Dadurch wird die Basisabfrage immer noch nur einmal ausgeführt. Es erhöht jedoch den Overhead der Abfrage und zahlt sich nur aus, wenn dies weniger ist als das Wiederholen der Basisabfrage für die Zählung.
Wenn Indizes verfügbar sind, die die endgültige Sortierreihenfolge unterstützen, kann es sich lohnen, die
ORDER BY
in den CTE aufzunehmen (redundant).quelle
MATERIALIZED
standardmäßig ist und zweimal referenziert wird.)Bearbeiten: Diese Antwort ist gültig, wenn die ungefilterte Tabelle abgerufen wird. Ich werde es zulassen, falls es jemandem helfen könnte, aber es könnte die ursprüngliche Frage nicht genau beantworten.
Die Antwort von Erwin Brandstetter ist perfekt, wenn Sie einen genauen Wert benötigen. Bei großen Tischen benötigen Sie jedoch oft nur eine ziemlich gute Annäherung. Postgres bietet Ihnen genau das und es wird viel schneller sein, da nicht jede Zeile ausgewertet werden muss:
SELECT * FROM ( SELECT * FROM tbl WHERE /* something */ ORDER BY /* something */ OFFSET ? LIMIT ? ) data RIGHT JOIN (SELECT reltuples FROM pg_class WHERE relname = 'tbl') pg_count(total_count) ON true;
Ich bin mir eigentlich nicht sicher, ob es einen Vorteil gibt, das zu externalisieren
RIGHT JOIN
oder es wie in einer Standardabfrage zu haben. Es würde einige Tests verdienen.SELECT t.*, pgc.reltuples AS total_count FROM tbl as t RIGHT JOIN pg_class pgc ON pgc.relname = 'tbl' WHERE /* something */ ORDER BY /* something */ OFFSET ? LIMIT ?
quelle
WHERE
Klausel in Ihren Abfragen widerlegt wird . Die zweite Abfrage ist logisch falsch (ruft eine Zeile für jede Tabelle in der Datenbank ab) - und teurer, wenn sie behoben ist.Es ist eine schlechte Praxis, zweimal dieselbe Abfrage für Just aufzurufen, um die Gesamtzahl der Zeilen des Returend-Ergebnisses zu erhalten. Es dauert Ausführungszeit und verschwendet die Serverressource.
Besser, Sie können
SQL_CALC_FOUND_ROWS
in der Abfrage verwenden, die MySQL anweist, die Gesamtzahl der Zeilen zusammen mit den Ergebnissen der Grenzwertabfrage abzurufen.Beispiel gesetzt als:
SELECT SQL_CALC_FOUND_ROWS employeeName, phoneNumber FROM employee WHERE employeeName LIKE 'a%' LIMIT 10; SELECT FOUND_ROWS();
Fügen Sie in der obigen Abfrage einfach die
SQL_CALC_FOUND_ROWS
Option in der restlichen erforderlichen Abfrage hinzu und führen Sie die zweite Zeile aus, dh geben SieSELECT FOUND_ROWS()
die Anzahl der Zeilen in der von dieser Anweisung zurückgegebenen Ergebnismenge zurück.quelle
Nein.
Es gibt vielleicht einen kleinen Gewinn, den Sie theoretisch gewinnen könnten, wenn Sie sie einzeln mit genügend komplizierten Maschinen unter der Haube betreiben. Wenn Sie jedoch wissen möchten, wie viele Zeilen einer Bedingung entsprechen, müssen Sie sie zählen und nicht nur eine BEGRENZTE Teilmenge.
quelle