Question

Lorsque vous parcourez les données d'une base de données, vous devez savoir combien de pages il y aura pour rendre les contrôles de saut de page.

Actuellement, je le fais en exécutant la requête deux fois, une fois encapsulée dans un count() pour déterminer le total des résultats, et une seconde fois avec une limite appliquée pour obtenir uniquement les résultats dont j'ai besoin pour la page en cours.

Cela semble inefficace. Existe-t-il un meilleur moyen de déterminer le nombre de résultats qui auraient été renvoyés avant que LIMIT ne soit appliqué?

J'utilise PHP et Postgres.

Était-ce utile?

La solution

SQL pur

Les choses ont changé depuis 2008. Vous pouvez utiliser une fonction de fenêtre pour obtenir le nombre complet et le résultat limité en une requête. (Introduit avec PostgreSQL 8.4 en 2009 ).

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

Notez que cela peut être considérablement plus coûteux que sans le décompte total. Toutes les lignes doivent être comptées et un éventuel raccourci ne prenant que les premières lignes d'un index correspondant peut ne plus être utile.
Peu importe avec les petites tables ou full_count & Lt; = OFFSET + LIMIT. C’est important pour un WHERE.

Corner : lorsque JOIN est au moins égal au nombre de lignes de la requête de base, aucune ligne est renvoyé. Donc, vous n'obtenez pas non plus GROUP BY. Alternative possible:

Prenez en compte la séquence d'événements :

    La clause
  1. OVER (et les count(*) OVER() conditions, mais pas ici) filtrent les lignes qualifiantes de la ou des tables de base.

    (ORDER BY et les fonctions d'agrégation iraient ici.)

  2. Les fonctions de fenêtre sont appliquées à toutes les lignes qualifiantes (en fonction de la clause DISTINCT et de la spécification de cadre de la fonction). Le simple DISTINCT ON est basé sur toutes les lignes.

  3. pg_num_rows

    (<=> ou <=> iraient ici.)

  4. <=> / <=> sont appliqués en fonction de l'ordre établi pour sélectionner les lignes à renvoyer.

<=> / <=> devient de plus en plus inefficace avec un nombre croissant de lignes dans la table. Envisagez des approches alternatives si vous avez besoin de meilleures performances:

Alternatives pour obtenir le décompte final

Il existe différentes approches pour obtenir le nombre de lignes affectées ( et non le nombre total avant que <=> & et <=> aient été appliqués). Postgres a la comptabilité interne combien de lignes ont été affectées par la dernière commande SQL. Certains clients peuvent accéder à ces informations ou compter les lignes elles-mêmes (comme psql).

Par exemple, vous pouvez récupérer le nombre de lignes affectées dans plpgsql immédiatement après l'exécution d'une commande SQL avec:

GET DIAGNOSTICS integer_var = ROW_COUNT;

Détails dans le manuel.

Ou vous pouvez utiliser <=> dans PHP . Ou des fonctions similaires dans d'autres clients.

Connexes:

Autres conseils

Comme je l’ai décrit sur mon blog , MySQL dispose d'une fonctionnalité appelée SQL_CALC_FOUND_ROWS . Cela supprime la nécessité de faire la requête deux fois, mais il doit toujours la faire dans son intégralité, même si la clause limit l'aurait autorisée à s'arrêter plus tôt.

Pour autant que je sache, il n'existe aucune fonctionnalité similaire pour PostgreSQL. Une chose à surveiller lors de la pagination (la chose la plus courante pour laquelle LIMIT est utilisé à mon humble avis): faire un & «OFFSET 1000 LIMIT 10 &»; Cela signifie que la base de données doit extraire au moins 1010 lignes, même si elle n'en donne que 10. Une méthode plus performante consiste à mémoriser la valeur de la ligne que vous avez commandée pour la ligne précédente ( le 1000ème dans ce cas) et réécrivez la requête comme suit: " ... WHERE order_row > valeur_de_1000_ème LIMITE 10 &. L'avantage est que & "Order_row &"; est très probablement indexé (sinon, vous avez un problème). L’inconvénient est que, si de nouveaux éléments sont ajoutés entre les pages consultées, cela risque d’être un peu désynchronisé (mais cela risque de ne pas être observé par les visiteurs et d’être un gain de performances important).

Vous pouvez limiter les conséquences négatives sur les performances en n'exécutant pas la requête COUNT () à chaque fois. Cache le nombre de pages pour, disons 5 minutes avant que la requête ne soit exécutée à nouveau. Si vous ne voyez pas un très grand nombre d'insertions, cela devrait fonctionner correctement.

Puisque Postgres fait déjà un certain nombre de choses en cache, ce type de méthode n’est pas aussi inefficace qu’il semble. Ce n'est certainement pas doubler le temps d'exécution. Nous avons des minuteries intégrées dans notre couche de base de données, donc j’en ai vu les preuves.

Étant donné que vous avez besoin de savoir pour la pagination, je suggérerais d'exécuter la requête complète une fois, d'écrire les données sur le disque en tant que cache côté serveur, puis de les transférer via votre mécanisme de pagination.

Si vous exécutez la requête COUNT dans le but de décider de fournir les données à l'utilisateur ou non (c'est-à-dire s'il y a > X enregistrements, renvoyer une erreur), vous devez vous en tenir à l'approche COUNT.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top