没有双重查询的MySQL分页?

时间:2009-05-04 02:05:15

标签: mysql pagination double

我想知道是否有办法从MySQL查询中获取结果数量,同时限制结果。

分页的工作方式(据我所知),首先我做了像

这样的事情
query = SELECT COUNT(*) FROM `table` WHERE `some_condition`

获得num_rows(查询)后,我得到了结果数。但是为了实际限制我的结果,我必须做第二个查询,如:

query2 = SELECT COUNT(*) FROM `table` WHERE `some_condition` LIMIT 0, 10

我的问题:无论如何都要检索将给出的结果总数,并限制在单个查询中返回的结果?或者更有效的方法。谢谢!

9 个答案:

答案 0 :(得分:65)

答案 1 :(得分:61)

不,这就是有多少想要分页的应用程序必须这样做。它是可靠且防弹的,尽管它使查询两次。但你可以将计数缓存几秒钟,这将有很大帮助。

另一种方法是使用SQL_CALC_FOUND_ROWS子句然后调用SELECT FOUND_ROWS()。除了事后你必须进行FOUND_ROWS()调用之外,还有一个问题:有a bug in MySQL这会影响ORDER BY查询,使得它在大型表上比它慢得多两个查询的天真方法。

答案 2 :(得分:24)

避免双重查询的另一种方法是首先使用LIMIT子句获取当前页面的所有行,然后在检索到最大行数时再执行第二次COUNT(*)查询。

在许多应用程序中,最可能的结果是所有结果都适合一个页面,并且必须进行分页是例外而不是常态。在这些情况下,第一个查询将不会检索最大结果数。

例如,stackoverflow问题的答案很少溢出到第二页。对答案的评论很少超过5左右的限制,以便全部显示。

因此,在这些应用程序中,您只需先使用LIMIT进行查询,然后只要未达到该限制,就可以准确知道有多少行而无需执行第二次COUNT(*)查询 - 应涵盖大多数情况。

答案 3 :(得分:14)

在大多数情况下,在两个单独的查询中执行它比在一个查询中执行它要快得多且资源消耗更少,即使这似乎是违反直觉的。

如果使用SQL_CALC_FOUND_ROWS,那么对于大型表,它会使查询慢得多,甚至比执行两个查询慢得多,第一个使用COUNT(*),第二个使用LIMIT。这样做的原因是SQL_CALC_FOUND_ROWS导致在获取行之后而不是之前应用,因此它在应用限制之前获取所有可能结果的整个行。索引无法满足这一要求,因为它实际上是在获取数据。

如果采用两种查询方法,第一种只获取COUNT(*)而不是实际获取和实际数据,这可以更快地得到满足,因为它通常可以使用索引而不必获取实际它所查看的每一行的行数据。然后,第二个查询只需要查看第一个$ offset + $ limit行,然后返回。

来自MySQL性能博客的这篇文章进一步解释了这一点:

http://www.mysqlperformanceblog.com/2007/08/28/to-sql_calc_found_rows-or-not-to-sql_calc_found_rows/

有关优化分页的详细信息,请查看this postthis post

答案 4 :(得分:3)

对于任何在2020年寻求答案的人。根据MySQL文档:

“ SQL_CALC_FOUND_ROWS查询修饰符和随附的FOUND_ROWS()函数自MySQL 8.0.17起已弃用,并将在将来的MySQL版本中删除。作为替代,考虑到使用LIMIT执行查询,然后使用COUNT(*)且不使用LIMIT的第二个查询来确定是否还有其他行。”

我想这解决了。

https://dev.mysql.com/doc/refman/8.0/en/information-functions.html#function_found-rows

答案 5 :(得分:2)

query = SELECT col, col2, (SELECT COUNT(*) FROM `table`) AS total FROM `table` WHERE `some_condition` LIMIT 0, 10

答案 6 :(得分:2)

我的回答可能会迟到,但您可以跳过第二个查询(带限制),只需通过后端脚本过滤信息。例如,在PHP中,您可以执行以下操作:

if($queryResult > 0) {
   $counter = 0;
   foreach($queryResult AS $result) {
       if($counter >= $startAt AND $counter < $numOfRows) {
            //do what you want here
       }
   $counter++;
   }
}

但是,当然,当你需要考虑数千条记录时,它会变得非常低效。预先计算好的计数可能是个好主意。

以下是关于这个主题的好读物: http://www.percona.com/ppc2009/PPC2009_mysql_pagination.pdf

答案 7 :(得分:0)

您可以在子查询中重用大部分查询并将其设置为标识符。例如,在运行时查找包含字母's'排序的电影的电影查询在我的网站上看起来像这样。

SELECT Movie.*, (
    SELECT Count(1) FROM Movie
        INNER JOIN MovieGenre 
        ON MovieGenre.MovieId = Movie.Id AND MovieGenre.GenreId = 11
    WHERE Title LIKE '%s%'
) AS Count FROM Movie 
    INNER JOIN MovieGenre 
    ON MovieGenre.MovieId = Movie.Id AND MovieGenre.GenreId = 11
WHERE Title LIKE '%s%' LIMIT 8;

请注意我不是数据库专家,我希望有人能够更好地优化它。当它直接从SQL命令行界面运行时,它们在我的笔记本电脑上都需要约0.02秒。

答案 8 :(得分:-13)

SELECT * 
FROM table 
WHERE some_condition 
ORDER BY RAND()
LIMIT 0, 10