À des fins de pagination, j'ai besoin d'exécuter une requête avec les clauses LIMIT
et OFFSET
. Mais j'ai également besoin d'un décompte du nombre de lignes qui seraient renvoyées par cette requête sans les clauses LIMIT
et OFFSET
.
Je veux courir:
SELECT * FROM table WHERE /* whatever */ ORDER BY col1 LIMIT ? OFFSET ?
Et:
SELECT COUNT(*) FROM table WHERE /* whatever */
À la fois. Existe-t-il un moyen de le faire, en particulier un moyen qui permet à Postgres de l'optimiser, de sorte qu'il soit plus rapide que d'exécuter les deux individuellement?
Oui. Avec une simple fonction de fenêtre:
SELECT *, count(*) OVER() AS full_count
FROM tbl
WHERE /* whatever */
ORDER BY col1
LIMIT ?
OFFSET ?
Sachez que le coût sera considérablement plus élevé que sans le nombre total, mais toujours moins cher que deux requêtes distinctes. Postgres doit en fait compter toutes les lignes dans les deux cas, ce qui impose un coût en fonction du nombre total de lignes éligibles. Détails:
Cependant , comme Dani l'a souligné , lorsque OFFSET
est au moins aussi grand que le nombre de lignes renvoyées à partir de la requête de base, aucune ligne n'est renvoyée. Nous n'obtenons donc pas non plus le full_count
.
Si ce n'est pas acceptable, une solution de contournement possible qui renvoie toujours le nombre complet serait avec un CTE et un 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;
Vous obtenez une ligne de valeurs NULL avec le full_count
ajouté si OFFSET
est trop grand. Ou il est ajouté à chaque ligne comme dans la première requête.
Si une ligne avec toutes les valeurs NULL est un résultat valide possible, vous devez vérifier offset >= full_count
pour lever l'ambiguïté de l'origine de la ligne vide.
Cela exécute toujours la requête de base une seule fois. Mais cela ajoute plus de frais généraux à la requête et ne paie que si c'est moins que de répéter la requête de base pour le nombre.
Si des index prenant en charge l'ordre de tri final sont disponibles, il peut être utile d'inclure le ORDER BY
dans le CTE (redondant).