MySQL:计算行数的最快方法


117

在MySQL中,哪种方式计算行数应该更快?

这个:

SELECT COUNT(*) FROM ... WHERE ...

或者,替代方案:

SELECT 1 FROM ... WHERE ...

// and then count the results with a built-in function, e.g. in PHP mysql_num_rows()

有人会认为第一种方法应该更快,因为在内部确定类似情况时,这显然是数据库领域,而数据库引擎应该比其他任何人都要快。


1
哦,我发现了一个类似的问题(stackoverflow.com/questions/1855226/…)。但是然后,我使用SELECT 1而不是SELECT *。有区别吗?
弗朗兹

我不知道,但是可以想象这两个答案是相同的-mysql查询优化器可能在每个答案上做同样的事情。那说前者不如后者含糊。您为什么不编写一些基准并对其进行测试?
杰西·科恩

嗯,让我们假设我正在尝试通过用不同的词来问类似的问题来提高SO的搜索引擎可见性;)
Franz

1
区别在于发送到PHP端的数据量。列越多,SELECT *相对于SELECT 1的速度就越慢,因为检索所有列而不是仅检索数字1。mysql_query()例如,在运行时,无论您使用什么方法,整个结果集都会从MySQL发送到PHP。处理这些数据。
toon81

提出这样的问题是获得洞察力或提出新想法的好方法,但是最终,如果您确实有特定的情况需要提高速度,则必须运行测试以了解最快的方法。
still_dreaming_1 2015年

Answers:


124

当您COUNT(*)使用count列索引时,它将是最好的结果。使用MyISAM引擎的Mysql 实际上存储行数,每次尝试对所有行进行计数时,它都不会对所有行进行计数。(基于主键的列)

使用PHP对行进行计数不是很聪明,因为您必须将数据从mysql发送到php。当您可以在mysql端实现相同的目的时,为什么要这样做呢?

如果COUNT(*)速度较慢,则应EXPLAIN在查询上运行,并检查是否确实使用了索引,以及应将索引添加到何处。


以下不是最快的方法,但是在某些情况下,COUNT(*)这并不适合-在开始对结果进行分组时,您可能会遇到问题,COUNT实际上并没有计算所有行。

解决方法是SQL_CALC_FOUND_ROWS。通常在选择行但仍需要知道总行数(例如,用于分页)时使用。选择数据行时,只需SQL_CALC_FOUND_ROWS在SELECT之后附加关键字:

SELECT SQL_CALC_FOUND_ROWS [needed fields or *] FROM table LIMIT 20 OFFSET 0;

选择所需的行后,可以通过以下单个查询获取计数:

SELECT FOUND_ROWS();

FOUND_ROWS() 必须在数据选择查询后立即调用。


总之,实际上,一切都取决于您有多少个条目以及WHERE语句中的内容。当有很多行(数万,数百万甚至更多)时,您应该真正注意索引的使用方式。


14
纠正:MyISAM存储行数。诸如此类的其他存储引擎InnoDB 存储行计数,并且每次都会计数所有行
Scrum Meister

1
你知道,这将是最快的,当你只是想找出是否有一行:SELECT 1 FROM ... LIMIT 1SELECT COUNT(*) FROM ...
弗朗兹(Franz)

1
注意如果您仍然需要数据并且只希望对分页/等进行计数,可能会很有用。获取数据然后计算程序中的行效率更高。
Tyzoid

6
引擎是否存储行数无关紧要。问题明确指出有一个WHERE子句。
阿尔瓦罗·冈萨雷斯

1
@Franz SELECT COUNT(*) FROM ...可能需要花费大量时间,具体取决于要扫描的内容(例如,非常大的表或数百万/数十亿/万亿行的索引)。 SELECT 1 FROM ... LIMIT 1立即返回,因为您将其限制在第一行。
jbo5112

59

与我的队友交谈后,里卡多告诉我们更快的方法是:

show table status like '<TABLE NAME>' \G

但是您必须记住,结果可能并不准确。

您也可以从命令行使用它:

$ mysqlshow --status <DATABASE> <TABLE NAME>

更多信息:http : //dev.mysql.com/doc/refman/5.7/en/show-table-status.html

您可以在mysqlperformanceblog上找到完整的讨论


2
对于InnoDB,这是一个近似值。
Martin Tournoij 2014年

2
当需要粗略了解非常大的表中的行数时,如果知道count(*)可能需要数小时,那真是太好了!
Mark Hansen

这使我免于拔掉所有的头发。COUNT(*)花了很长时间才能计算我数据库中的所有3300万行数据。无论如何,我只想知道并行删除行功能是否正常工作。我不需要一个确切的数字。
joemar.ct 2015年

1
+1使用表格状态代替“ COUNT(*)”应该是此问题的正确答案,因为它是关于“最快”而不是“准确性”的问题。
lepe 2015年

2
使用SHOW TABLE STATUS(或等同SELECTinformation_schema)速度很快,但它并不处理WHERE条款。对于MyISAM来说是精确的,但对于InnoDB来说则是不精确的(有时会减少2倍)。
里克·詹姆斯

29

好问题,好答案。如果有人正在阅读此页面但缺少该部分,这是一种回显结果的快速方法:

$counter = mysql_query("SELECT COUNT(*) AS id FROM table");
$num = mysql_fetch_array($counter);
$count = $num["id"];
echo("$count");

5
从PHP 5.5.0开始,mysql_query是已弃用的函数。
奥马尔·塔里克

8
为什么不as count呢?id乍一看令人困惑。
Orkhan Alikhanov

没有回答这个问题
心理的

17

该查询(类似于bayuah发布的内容)显示了数据库中所有表计数的不错摘要:(我强烈建议Ivan Cachicatari简化存储过程的版本)。

SELECT TABLE_NAME AS 'Table Name', TABLE_ROWS AS 'Rows' FROM information_schema.TABLES WHERE TABLES.TABLE_SCHEMA = '`YOURDBNAME`' AND TABLES.TABLE_TYPE = 'BASE TABLE'; 

例:

+-----------------+---------+
| Table Name      | Rows    |
+-----------------+---------+
| some_table      |   10278 |
| other_table     |     995 |

它给了我一个结果。但是count(1)的结果与此结果不同。与计数查询相比,这种方式总是提供更少的数字。有什么想法吗?
Ayyappan Sekar,

3
只是给读者的笔记。此方法非常快速,但是仅适用于行数近似的情况,因为其中存储的值information_schemaSELECT count(*) FROM使用InnoDB时返回的值不同。如果需要严格的值,请记住,此方法仅对MyISAM表提供严格的值。使用InnoDB,行数是近似值。
巴托斯·菲林

13

我一直都知道,以下内容将为我提供最快的响应时间。

SELECT COUNT(1) FROM ... WHERE ...

1
不会从...哪里选择1 ...甚至更快?
patrick

3
@patrick- SELECT 1 ...将返回与一样多的行WHERELIMIT要求它们,它们都将为“ 1”。
里克·詹姆斯

1
show table status like '<TABLE NAME>' 这样会更快。

@deep-但如果有WHERE子句,则不相关。而且,对于InnoDB,这只是一个估计。
瑞克·詹姆斯

@RickJames是的,真的!

6

如果需要获取整个结果集的计数,则可以采用以下方法:

SELECT SQL_CALC_FOUND_ROWS * FROM table_name LIMIT 5;
SELECT FOUND_ROWS();

通常这并不比使用速度快,COUNT尽管人们可能会认为情况恰恰相反,因为它是在内部进行计算并且不会将数据发送回用户,因此怀疑性能会提高。

进行这两个查询对于分页以获得总数很有好处,但对于使用WHERE子句而言却并非如此。


有趣。可以在最常见的数据库系统上工作吗?MySQL,Postgres,SQLite ...?
弗朗兹(Franz)

4
实际上,这通常根本不比完全使用COUNT(*)快。见stackoverflow.com/questions/186588/...
toon81

2
使用此功能时应非常小心。鲁use的使用曾经使我们的整个生产环境陷入停顿。这是非常耗费资源的,因此请谨慎使用。
Janis Peisenieks

6

我做了一些基准测试来比较COUNT(*)vs COUNT(id)(id是表的主键-索引)的执行时间。

试用次数:10 * 1000次查询

结果: COUNT(*)快7%

查看图:基准图

我的建议是使用: SELECT COUNT(*) FROM table


1
仅供参考,还有一种通用的计算方法COUNT(1),很有趣的是在那里看到一些基准...
Sliq

4

试试这个:

SELECT
    table_rows "Rows Count"
FROM
    information_schema.tables
WHERE
    table_name="Table_Name"
AND
    table_schema="Database_Name";

@lepe对不起。我的意思是,如果某位不赞成投票的人给出为什么要这样做的解释,这真是太好了,这样每个人都可以学到些什么。
bayuah

1
这将很快给您一个大概的答案。如果您需要确切的答案,则需要执行select count(*) from table_name其他操作。dba.stackexchange.com/questions/151769/…–
Programster

@Programster谢谢。总比让我呆在黑暗中近一年更好。
bayuah

1
@bayuah我不确定您的最后评论是什么意思。我只能假设您认为我是拒绝您回答的人,但我不是。
Programster

1
@Programster不,对不起,我不是那个意思。我的意思是感谢您的解释,因此我可以推测出当他/她这样做时,Downvoter可能会怎么想。
bayuah

3

也许您可能要考虑做一个SELECT max(Id) - min(Id) + 1。仅当您的ID是连续的且行未删除时,这才起作用。但是,它非常快。


3
请注意:服务器有时会使用自动增量值> 1(出于备份原因),因此此解决方案很好,但是您应该首先检查数据库配置。
Alex

1

EXPLAIN SELECT id FROM ....为我做了把戏。我可以看到rows结果列下的行数。


0

我为德国政府处理过桌子,有时有6000万条记录。

而且我们需要知道很多行。

因此,我们数据库程序员决定在每个表中始终有一个记录,该记录始终存储着总记录号。我们根据INSERT或DELETE行更新了该数字。

我们尝试了所有其他方式。这是迄今为止最快的方法。


1
您如何更新该行的详细信息是什么?这意味着尽管表的设计有误,但所有行都需要一个浪费的int才能进行。
提请

5
是的,那真是愚蠢的哈哈。对于每个查询,您都必须忽略第一行。我只创建一个总计表并根据触发器填充该表。用户表插入后,更新总计表。用户表上的删除,更新总计表。
HTMLGuy

-1

在主键上带有where条件的count(*)语句对我而言,返回行计数要快得多,从而避免了全表扫描。

SELECT COUNT(*) FROM ... WHERE <PRIMARY_KEY> IS NOT NULL;

对我来说,这比

SELECT COUNT(*) FROM ...
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.