Besonders bei der Implementierung einer Pagination ist der MySQL-Select-Modifier SQL_CALC_FOUND_ROWS eine große Hilfe. Mithilfe eines LIMIT Statements werden z.B. die Beiträge einer bestimmten Seite aus der Datenbank abgefragt. Darüber hinaus möchte man aber wissen, wieviele Beiträge es insgesamt gibt, um daraus die letzte Seite einer Pagination zu errechnen oder um zu wissen, ob es noch eine folgende Seite gibt, zu der weitergeblättert werden kann. Mit folgender Query werden beispielsweise die Beiträge 21 bis 30 abgefragt:
SELECT SQL_CALC_FOUND_ROWS
author, title, content, post_date
FROM posts
WHERE category = 42
ORDER BY post_date
LIMIT 20, 10;
Der Modifier SQL_CALC_FOUND_ROWS sorgt dafür, dass die Gesamtanzahl aller Beiträge, auf die das WHERE Statement zutrifft, in einer direkt folgenden Query mit der Funktion FOUND_ROWS() abgefragt werden kann:
SELECT FOUND_ROWS();
Mit der MySQL-Version 8.0.17 ist das Schlüsselwort SQL_CALC_FOUND_ROWS nun als deprecated gekennzeichnet, d.h. es sollte künftig nicht mehr verwendet werden, da es in einer späteren MySQL-Version entfernt werden wird, siehe hierzu Anmerkung im MySQL Reference Manual. Einige Optimierungen sollen demnach mit SQL_CALC_FOUND_ROWS nicht funktionieren. Stattdessen soll die Gesamtanzahl nun durch eine zweite Abfrage mit COUNT(*) ermittelt werden:
SELECT author, title, content, post_date
FROM posts
WHERE category = 42
ORDER BY post_date
LIMIT 20, 10;
SELECT COUNT(*)
FROM posts
WHERE category = 42;
Kommentare
Hmmm, da frage ich mich doch wie das in der Performance aussieht…
Wenn das result vom ersten Query nicht im QueryCache liegt, weil zu groß oder whatever, dürfte das vermutlich eine Ecke langsamer sein. Aber selbst wenn im Cache, wird nicht eh das gefilterte Result in den Cache gelegt? Mit MyISAM dürfte das noch recht fix sein durch die COUNT(*) Optimierungen, aber mit Inno?
Bei sehr vielen Ergebnissen (>100000) kann SQL_CALC_FOUND_ROWS zu extrem langen Abfragen führen und ggf. das Zeitlimit überschreiten. Dann hat man gar kein Ergebnis. Ohne SQL_CALC_FOUND_ROWS wären die ersten 10 Ergebnisse (bei LIMIT 10) sofort da, ein Timeout einer zweiten count(*) Abfrage würde weniger stören, da die ersten 10 Ergebnisse ja bereits da sind. Zudem kann man die where-Bedingung der zweiten Abfrage optimieren.