我应该不算(*)吗?


72

我知道执行这样的查询通常不是一个好主意:

SELECT * FROM `group_relations`

但是,当我只想计数时,我应该进行此查询,因为这允许更改表,但仍产生相同的结果。

SELECT COUNT(*) FROM `group_relations`

或更具体

SELECT COUNT(`group_id`) FROM `group_relations`

我觉得后者可能会更快,但是还有其他需要考虑的事情吗?

更新:在这种情况下,我使用的是InnoDB,对不起,因为它不够具体。


您应该真正指定要谈论的是MyISAM还是InnoDB(或其他)
itsadok

很好的问题,我之前也对此感到疑惑。
基普(Kip)

Answers:


102

如果所讨论的列不是NOT NULL,则两个查询都是等效的。当group_id包含空值时,

select count(*)

将计算所有行,而

select count(group_id)

只计算group_id不为null的行。

此外,某些数据库系统(例如MySQL)在您请求count(*)时会进行优化,从而使此类查询比特定查询快一点。

就个人而言,在进行计数时,我正在做count(*)以便安全使用null。


1
mysql处理计数的方式因所使用的数据库引擎而异。在此答案中,假定为MyISAM,但问题为INNODB。就前者而言,速度快且可缓存,而后者则相当慢。
乌合之众

2
是的,但是a)当我编写响应时,没有InnoDB注释; b)虽然InnoDB无法优化计数,但count(*)的速度并不比count(group_id)快或慢。甚至在MyISAM上,您添加WHERE子句时也会失去速度优势
pilif

另外,如果您想知道是否存在行,group_id则从group_relationsWHERE ROWNUM = 1中选择COUNT()
Dead Programmer,2010年


11

COUNT(*)计算所有行,而COUNT(column_name)将仅计算指定列中没有NULL值的行。

在MySQL中需要注意的重要事项:

在MyISAM表上,对于*或非空列,COUNT()非常快,因为已缓存了行数。InnoDB没有行计数缓存,因此无论该列是否可以为null,COUNT(*)或COUNT(column_name)的性能都没有差异。您可以在MySQL性能博客上阅读有关此文章差异的更多信息


1
也许是一个愚蠢的问题...我怎么知道我的mysql数据库是由innodb还是myisam支持的?我在共享服务器上运行,所以我没有自己设置...
Kip

1
InnoDB和MyISAM是表存储引擎,MySQL有其中几个-dev.mysql.com/doc/refman/5.0/en/storage-engines.html。您的数据库可以混合使用不同表的存储引擎。看看您的phpMyAdmin,检查您不同表的存储引擎
Eran Galperin

8

如果尝试使用SELECT COUNT(1) FROMgroup_relations,则速度会更快一些,因为它将不会尝试从列中检索信息。

编辑:我只是做了一些研究,发现这仅在某些数据库中发生。在sqlserver中,使用1或*相同,但是在oracle上,使用1更快。

http://social.msdn.microsoft.com/forums/zh-CN/transactsql/thread/9367c580-087a-4fc1-bf88-91a51a4ee018/

显然,它们在mysql中没有区别,例如sqlserver,解析器似乎将查询更改为select(1)。抱歉,如果我以某种方式误导您。


比COUNT()还吗?为什么COUNT()必须从列中检索信息?
肯特·布加亚特

如果MySQL检索count(*)的列信息,那将是避免将其作为DBMS的一个很好的理由。我不敢相信开发人员会那么愚蠢。
paxdiablo,

5

我自己对此很好奇。阅读文档和理论答案都很好,但是我想在经验和证据之间取得平衡。

我有一个MySQL表(InnoDB),其中有5,607,997条记录。该表位于我自己的专用沙箱中,因此我知道内容是静态的,没有其他人在使用服务器。我认为这有效地消除了所有对性能的影响。我有一个带有auto_increment主键字段(Id)的表,我知道该表永远不会为空,将用于我的where子句测试(WHERE ID不为NULL)。

我在运行测试中看到的唯一其他可能的故障是缓存。第一次运行查询总是比使用相同索引的后续查询慢。我在下面将其称为“缓存种子”调用。只是将其混为一谈,我使用where子句运行了该子句,我知道无论任何数据(TRUE = TRUE),该子句都将始终为true。

那就是我的结果:

查询类型

      |  w/o WHERE          | where id is not null |  where true=true

计数()

      |  9 min 30.13 sec ++ | 6 min 16.68 sec ++   | 2 min 21.80 sec ++
      |  6 min 13.34 sec    | 1 min 36.02 sec      | 2 min 0.11 sec 
      |  6 min 10.06 se     | 1 min 33.47 sec      | 1 min 50.54 sec

COUNT(编号)

      |  5 min 59.87 sec    | 1 min 34.47 sec      | 2 min 3.96 sec 
      |  5 min 44.95 sec    | 1 min 13.09 sec      | 2 min 6.48 sec

COUNT(1)

      | 6 min 49.64 sec    | 2 min 0.80 sec       | 2 min 11.64 sec
      | 6 min 31.64 sec    | 1 min 41.19 sec      | 1 min 43.51 sec

++这被视为缓存种子调用。预计它会比其余的慢。

我会说结果说明一切。COUNT(Id)通常会淘汰其他的。即使添加了Where子句,即使您知道该子句的评估结果为true,它也会大大减少访问时间。最有效的点似乎是COUNT(Id)... WHERE Id NOT NULL。

我希望看到其他人的结果,也许使用较小的表,或者使用针对不同字段的where子句而不是您要计算的字段。我敢肯定,我还没有考虑其他变化。


有趣的测试。我试图使用Innodb在Mysql 5.1.45上使用超过8000万条记录的表来复制您的结果。我的结果有很大不同,没有Where子句的count()比任何其他方法快20%。添加Where子句总是较慢。我的服务器可以在15.2秒内计数()8000万行。我也有一个auto-inc unsigned int作为主键。
加里2010年

4

寻求替代方案

如您所见,当表变大时,COUNT查询变慢。我认为最重要的是考虑要解决的问题的性质。例如,许多开发人员COUNT在为大型记录集生成分页时使用查询,以便确定结果集中的页面总数。

知道COUNT查询的增长速度会很慢,您可以考虑使用另一种显示分页控件的方法,该方法可以让您避开慢速查询。Google的分页就是一个很好的例子。

非正规化

如果您绝对必须知道与特定计数匹配的记录数,请考虑经典的数据非规范化技术。与其在查找时不计算行数,不如考虑在记录插入时增加一个计数器,并在删除记录时减少该计数器。

如果您决定执行此操作,请考虑使用幂等事务性操作来使这些非规范化值保持同步。

BEGIN TRANSACTION;
INSERT INTO  `group_relations` (`group_id`) VALUES (1);
UPDATE `group_relations_count` SET `count` = `count` + 1;
COMMIT;

或者,如果您的RDBMS支持数据库触发器,则可以使用它们。

根据您的体系结构,使用诸如memcached之类的缓存层来存储,递增和递减非规范化值可能很有意义,并且当缺少高速缓存键时,只需进入慢速COUNT查询即可。如果数据非常不稳定,这可以减少总体写入争用,尽管在这种情况下,您可能需要考虑解决“狗堆效应”的方法



2

COUNT中的星号与用于选择表的所有字段的星号无关。说COUNT(*)比COUNT(field)慢是纯粹的垃圾

我认为选择COUNT(*)比选择COUNT(field)更快。如果RDBMS检测到您在COUNT(而不是字段)上指定“ *”,则无需评估任何内容即可增加计数。而如果您在COUNT上指定字段,则RDBMS将始终评估您的字段是否为空或不对其进行计数。

但是,如果您的字段可以为空,请在COUNT中指定该字段。


2

COUNT(*)个事实和神话:

误解:“ InnoDB无法很好地处理count(*)查询”:

如果您有WHERE子句,大多数count(*)查询将由所有存储引擎以相同的方式执行,否则InnoDB将必须执行全表扫描。

事实:如果没有where子句,InnoDB不会优化count(*)查询


2

最好通过索引列(例如主键)进行计数。

SELECT COUNT(`group_id`) FROM `group_relations`

1

正如塞巴斯蒂安(Sebastian)所说的,这应该取决于您实际想要实现的目标,即明确您的意图!如果你刚刚计数行,然后去COUNT(*),或计数的COUNT(列)一列去。

也可能值得检查您的数据库供应商。回到我以前使用Informix的时候,它对COUNT(*)进行了优化,与对单个或多个列进行计数相比,查询计划的执行成本为1,这将导致更高的数字


1

如果您尝试从group_relations选择SELECT COUNT(1),则速度会更快一些,因为它不会尝试从列中检索信息。

COUNT(1)过去比COUNT(*)更快,但现在已经不对了,因为现代DBMS足够聪明,可以知道您不想了解列


1

从MySQL获得的关于此类事情的建议通常是,从长远来看,尝试基于此类技巧优化查询可能是一个诅咒。在MySQL的历史上有一些例子,其中依赖于优化器如何工作的某人的高性能技术最终成为了下一版本的瓶颈。

编写查询以回答您所提出的问题-如果要计数所有行,请使用COUNT(*)。如果要计数非空列,请使用COUNT(col)WHERE col IS NOT NULL。适当地建立索引,并将优化留给优化器。尝试进行自己的查询级优化有时会使内置优化器的效率降低。

就是说,您可以在查询中做一些事情,以使优化程序更轻松地加速它,但是我不认为COUNT是其中之一。

编辑:上面答案中的统计数据很有趣。在这种情况下,我不确定优化器中是否确实有工作。我只是在谈论一般的查询级优化。


0

我知道执行这样的查询通常不是一个好主意:

SELECT * FROM `group_relations`

但是,当我只想计数时,我应该进行此查询,因为这允许更改表,但仍产生相同的结果。

SELECT COUNT(*) FROM `group_relations`

正如您的问题所暗示的那样,SELECT *不明智的原因是对表的更改可能需要对代码进行更改。不适用于COUNT(*)。想要SELECT COUNT('group_id')给您的特殊行为很少见-通常您想知道记录的数量。那就是COUNT(*)目的,所以使用它。

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.