121

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

分页的工作方式(据我所知),首先我做类似的事情

query = SELECT COUNT(*) FROM `table` WHERE `some_condition`

在我得到 num_rows(query) 之后,我就有了结果的数量。但是为了实际限制我的结果,我必须执行第二个查询,例如:

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

我的问题:是否有既要检索将给出的结果总数,又要限制在单个查询中返回的结果?或者任何更有效的方式来做到这一点。谢谢!

4

9 回答 9

75

我几乎从不做两个查询。

只需返回比需要的多行,页面上只显示 10 行,如果显示多行,则显示“下一步”按钮。

SELECT x, y, z FROM `table` WHERE `some_condition` LIMIT 0, 11
// iterate through and display 10 rows.

// if there were 11 rows, display a "Next" button.

您的查询应该以最相关的顺序返回。很可能,大多数人不会关心从 412 页中转到第 236 页。

当您进行谷歌搜索时,您的结果不在第一页上,您可能会转到第二页,而不是第九页。

于 2010-07-24T11:14:50.743 回答
71

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

另一种方法是使用SQL_CALC_FOUND_ROWS从句,然后调用SELECT FOUND_ROWS(). 除了您必须在FOUND_ROWS()之后进行调用之外,还有一个问题:MySQL中有一个错误,它会影响ORDER BY查询,使其在大型表上比两个查询的幼稚方法慢得多。

于 2009-05-04T02:37:15.047 回答
29

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

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

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

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

于 2011-09-08T05:19:15.637 回答
16

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

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

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

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

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

有关优化分页的更多信息,请查看这篇文章这篇文章

于 2009-05-04T03:46:54.207 回答
6

对于任何在 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

于 2020-06-11T15:54:49.010 回答
2
query = SELECT col, col2, (SELECT COUNT(*) FROM `table`)/10 AS total FROM `table` WHERE `some_condition` LIMIT 0, 10

其中 10 是页面大小,0 是页码(需要在查询中使用 pageNumber-1)

于 2009-05-04T02:21:22.680 回答
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

于 2012-04-09T05:42:56.670 回答
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 秒。

于 2016-07-16T11:26:41.047 回答
-18
SELECT * 
FROM table 
WHERE some_condition 
ORDER BY RAND()
LIMIT 0, 10
于 2012-10-29T03:28:03.343 回答