哪个是最快的? 是 SELECT SQL _ CALC _ FOEND _ ROWS FROM‘ table’,还是 SELECT COUNT (*)

当限制 SQL 查询(通常用于分页)返回的行数时,有两种方法可以确定记录总数:

方法1

在原来的 SELECT中包含 SQL_CALC_FOUND_ROWS选项,然后通过运行 SELECT FOUND_ROWS()得到行的总数:

SELECT SQL_CALC_FOUND_ROWS * FROM table WHERE id > 100 LIMIT 10;
SELECT FOUND_ROWS();

方法2

正常运行查询,然后通过运行 SELECT COUNT(*)获得总行数

SELECT * FROM table WHERE id > 100 LIMIT 10;
SELECT COUNT(*) FROM table WHERE id > 100;

哪种方法最好/最快?

150986 次浏览

这取决于具体情况。请参阅 MySQL 性能博客上关于这个主题的文章: SQL_CALC_FOUND_ROWS还是不到 SQL_CALC_FOUND_ROWS

简单总结一下: Peter 说这取决于你的索引和其他因素。这篇文章的许多评论似乎说,SQL_CALC_FOUND_ROWS几乎总是比运行两个查询慢,有时甚至慢10倍。

在选择“最佳”方法时,一个比速度更重要的考虑因素可能是代码的可维护性和正确性。如果是这样,SQL _ CALC _ FOUNDROWS 更可取,因为您只需要维护一个查询。使用单个查询完全排除了 main 查询和 COUNT 查询之间存在细微差别的可能性,这可能导致 COUNT 不准确。

恕我直言,2个问题的原因

SELECT * FROM count_test WHERE b = 666 ORDER BY c LIMIT 5;
SELECT count(*) FROM count_test WHERE b = 666;

比使用 SQL _ CALC _ FOEND _ ROWS 更快

SELECT SQL_CALC_FOUND_ROWS * FROM count_test WHERE b = 555 ORDER BY c LIMIT 5;

必须被视为特殊情况。

实际上,它取决于 WHERE 子句的选择性,而不是等效于 ORDER + LIMIT 的隐式 WHERE 子句的选择性。

正如 Arvids 在评论(http://www.mysqlperformanceblog.com/2007/08/28/to-sql_calc_found_rows-or-not-to-sql_calc_found_rows/#comment-1174394)中所说,EXPLAIN 使用或不使用临时表的事实应该是了解 SCFR 是否会更快的一个很好的基础。

但是,正如我加上的(http://www.mysqlperformanceblog.com/2007/08/28/to-sql_calc_found_rows-or-not-to-sql_calc_found_rows/#comment-8166482) ,结果真的,真的取决于情况。对于特定的分页器,您可以得出这样的结论: “对于前3个页面,使用2个查询; 对于后面的页面,使用 SCFR”!

根据以下文章: https://www.percona.com/blog/2007/08/28/to-sql_calc_found_rows-or-not-to-sql_calc_found_rows/

如果 where 子句上有一个 索引(如果在您的情况下 id 是索引的) ,那么最好不要使用 SQL _ CALC _ FOound _ ROWS,而是使用2个查询,但是如果您没有关于在 where 子句中放入什么的索引(在您的情况下是 id) ,那么使用 SQL _ CALC _ FOound _ ROWS会更有效率。

删除一些不必要的 SQL,然后 COUNT(*)会比 SQL_CALC_FOUND_ROWS快。例如:

SELECT Person.Id, Person.Name, Job.Description, Card.Number
FROM Person
JOIN Job ON Job.Id = Person.Job_Id
LEFT JOIN Card ON Card.Person_Id = Person.Id
WHERE Job.Name = 'WEB Developer'
ORDER BY Person.Name

然后不要计算不必要的部分:

SELECT COUNT(*)
FROM Person
JOIN Job ON Job.Id = Person.Job_Id
WHERE Job.Name = 'WEB Developer'

MySQL 已经开始在8.0.17以后的版本中取消 SQL_CALC_FOUND_ROWS的功能。

因此,总是喜欢可以考虑使用 LIMIT执行查询,然后使用 COUNT(*)执行第二个查询,而不使用 LIMIT来确定是否存在其他行。

来自 医生:

SQL _ CALC _ FOEND _ ROWS 查询修饰符以及附带的 FOEND _ ROWS () 函数从 MySQL 8.0.17开始就不推荐使用,并将在 未来的 MySQL 版本。

COUNT (*)受到某些优化的影响 导致一些优化被禁用。

使用以下查询:

SELECT * FROM tbl_name WHERE id > 100 LIMIT 10;
SELECT COUNT(*) WHERE id > 100;

此外,据观察,SQL_CALC_FOUND_ROWS一般有更多的问题,如 MySQL WL # 12615所解释的:

SQL _ CALC _ FOEND _ ROWS 有很多问题,首先,它很慢。 通常,使用 LIMIT 运行查询,然后使用 单独的选择计数() ,因为 COUNT ()可以使 使用在搜索整个 结果集(例如,对于 COUNT (*) ,可以跳过文件排序,而对于 CALC _ FOEND _ ROWS,我们必须禁用一些文件排序优化,以 保证正确的结果)

更重要的是,在许多 特别是当一个查询有多个查询块时 (例如使用 UNION) ,根本没有办法计算 同时生成一个有效的查询 迭代器执行器正在向这些类型的查询发展, 要想保持同样的语义真的很难。 此外,如果查询中有多个 LIMIT (例如 派生表) ,不一定清楚它们中的哪一个 SQL _ CALC _ FOEND _ ROWS 应该引用 在迭代器执行器中必然会得到不同的语义 相比之下,他们以前有什么。

最后,大多数 SQL _ CALC _ FOEND _ ROWS 的用例似乎 有用的应该简单地通过其他机制而不是 LIMIT/OffSET 来解决。 例如,一本电话簿应该按字母分页(在用户体验方面都是如此) 以及索引使用情况) ,而不是按记录编号 越来越多的无限滚动按日期排序(再次允许索引 使用) ,不按邮编分页。等等。

你还有其他的选择:

窗口函数将直接返回实际的大小(在 MariaDB 中测试) :

SELECT
`mytable`.*,
COUNT(*) OVER() AS `total_count`
FROM `mytable`
ORDER BY `mycol`
LIMIT 10, 20

2)跳出框架思考,大多数时候用户不需要知道表的 没错大小,一个近似值往往就足够了。

SELECT `TABLE_ROWS` AS `rows_approx`
FROM `INFORMATION_SCHEMA`.`TABLES`
WHERE `TABLE_SCHEMA` = DATABASE()
AND `TABLE_TYPE` = "BASE TABLE"
AND `TABLE_NAME` = ?

表上有2.000.000行的简单示例和如下查询:

select fieldname
from table_add
where
descryption_per like '%marihuana%'
or addiction_per like '%alkohol%';

它是一个完整的表扫描每一个查询-所以它需要时间 x 2。 我的意思是“选择计数(*)从... ..。