MySQL pagination sans double interrogation?
Je me demandais si il y avait un moyen pour obtenir le nombre de résultats pour une requête MySQL, et en même temps de limiter les résultats.
La façon dont la pagination fonctionne (ce que je comprends), j'ai d'abord faire quelque chose comme
query = SELECT COUNT(*) FROM `table` WHERE `some_condition`
Après je reçois le num_rows(requête), j'ai le nombre de résultats. Mais de là à en fait la limite de mes résultats, je dois faire une deuxième requête comme:
query2 = SELECT COUNT(*) FROM `table` WHERE `some_condition` LIMIT 0, 10
Ma question: Est-il de toute façon à la fois de récupérer le nombre total de résultats qui serait donné, ET de limiter les résultats renvoyés en une seule requête? Ou de toute façon la plus efficace de le faire. Merci!
- Bien que vous ne l'auriez pas COUNT(*) dans query2
Vous devez vous connecter pour publier un commentaire.
Pas, c'est combien les applications qui veulent paginer le faire. Il est fiable et à l'épreuve des balles, mais il fait la requête à deux reprises. Mais vous pouvez mettre en cache le comte de quelques secondes et ça va nous aider beaucoup.
L'autre façon est d'utiliser des
SQL_CALC_FOUND_ROWS
clause et ensuite appelerSELECT FOUND_ROWS()
. outre le fait que vous devez mettre laFOUND_ROWS()
appel par la suite, il y a un problème avec cela: Il y a un bug dans MySQL que ce chatouille qui affecteORDER BY
requêtes rendant beaucoup plus lent sur les grandes tables de l'approche naïve de deux requêtes.Je n'ai presque jamais faire deux requêtes.
Il suffit de retourner un plus de lignes que cela est nécessaire, seulement l'affichage 10 sur la page, et si il y a plus que ce qui est affiché, l'affichage d'un bouton "Suivant".
Votre requête doit retourner dans un ordre des plus pertinents en premier. Les Chances sont, la plupart des gens ne vont pas soin d'aller à la page 236 de 412.
Lorsque vous faites une recherche sur google, et vos résultats ne sont pas sur la première page, vous aurez probablement aller à la page deux, pas neuf.
COUNT
est une fonction d'agrégation. Comment pouvez vous retourner le comte et tous les résultats dans une seule requête? La requête ci-dessus retournera seulement 1 ligne, peu importe ce que lesLIMIT
est fixé à. Si vous ajoutezGROUP BY
, il va retourner tous les résultats, mais laCOUNT
sera inexacteUne autre approche pour éviter la double interrogation est de récupérer toutes les lignes de la page actuelle à l'aide d'une LIMITE de la clause d'abord, puis seulement faire un second compte(*) de la requête si le nombre maximal de lignes ont été récupérés.
Dans de nombreuses applications, le résultat le plus probable sera que tous les résultats sur une seule page, et d'avoir à faire la pagination est l'exception plutôt que la norme. Dans ces cas, la première requête ne va pas récupérer le nombre maximum de résultats.
Par exemple, les réponses sur stackoverflow question rarement répandre sur une deuxième page. Commentaires sur la réponse rarement déborder la limite de 5 ou si nécessaire de leur montrer toutes.
Donc, dans ces applications, vous pouvez tout simplement faire une requête avec une LIMITE tout d'abord, et puis, tant que cette limite n'est pas atteinte, vous savez exactement combien de lignes il y a, sans le besoin de faire un second compte(*) une requête qui devrait couvrir la majorité des situations.
Dans la plupart des situations, il est beaucoup plus rapide et consomme moins de ressources pour le faire dans les deux requêtes distinctes que de le faire en une seule, même si cela semble contre-intuitif.
Si vous utilisez SQL_CALC_FOUND_ROWS, puis pour les grandes tables, il rend votre requête beaucoup plus lent, beaucoup plus lent même que l'exécution de deux requêtes, la première avec un COUNT(*) et la deuxième avec une LIMITE. La raison pour cela est que SQL_CALC_FOUND_ROWS provoque la clause LIMIT être appliquée après de l'extraction de l'lignes au lieu de l'avant, de sorte qu'il récupère l'ensemble de la ligne de tous les résultats possibles avant d'appliquer les limites. Cela ne peut pas être satisfait par un indice parce qu'il va chercher les données.
Si vous prenez les deux requêtes approche, seul le premier de l'extraction de COUNT(*) et pas vraiment de l'extraction et de données réelles, ce qui peut être satisfait beaucoup plus vite, car il est généralement possible d'utiliser des indices et n'ont pas à aller chercher la ligne réelle des données pour chaque ligne qu'il regarde. Ensuite, la deuxième requête suffit de regarder le premier $offset+$limite de lignes et de revenir ensuite.
Ce post à partir de la base de la performance blog explique cette plus loin:
http://www.mysqlperformanceblog.com/2007/08/28/to-sql_calc_found_rows-or-not-to-sql_calc_found_rows/
Pour plus d'informations sur l'optimisation de la pagination, vérifiez ce post et ce post.
where
clause à l'intérieur de la requête et vous obtenez le droit "total", avec les résultats paginées (page est sélectionnée avec lelimit
clauseMa réponse peut être en retard, mais vous pouvez sauter la deuxième requête (à la limite) et juste de filtrer l'info par le biais de votre retour en fin de script. En PHP par exemple, vous pourriez faire quelque chose comme:
Mais bien sûr, lorsque vous avez des milliers de dossiers à examiner, il devient inefficace très rapide. Pré-calculée à compter peut-être une bonne idée de regarder dans.
Voici une bonne lecture sur le sujet:
http://www.percona.com/ppc2009/PPC2009_mysql_pagination.pdf
Vous pouvez réutiliser la plupart de la requête dans une sous-requête et de définir un identifiant. Par exemple, un film de requête qui trouve des films contenant la lettre 's' de la commande par le moteur d'exécution devrait ressembler à ceci sur mon site.
Ne remarque que je ne suis pas un expert base de données, et j'espère que quelqu'un sera en mesure d'optimiser un peu mieux. Comme il se exécute directement à partir de la ligne de commande SQL interface ils ont tous deux se ~0.02 secondes sur mon ordinateur portable.