我应该COUNT(*)吗?

时间:2009-01-19 11:07:27

标签: mysql performance innodb

我知道做这样的查询通常是个坏主意:

SELECT * FROM `group_relations`

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

SELECT COUNT(*) FROM `group_relations`

或更具特色的

SELECT COUNT(`group_id`) FROM `group_relations`

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

更新:在这种情况下我使用InnoDB,抱歉没有更具体。

14 个答案:

答案 0 :(得分:100)

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

select count(*)

将计算所有行,而

select count(group_id)

只计算group_id不为空的行。

此外,某些数据库系统(如MySQL)在请求count(*)时会使用优化,这使得此类查询比特定查询快一点。

就个人而言,在计算时,我正在使用空值来计算(*)以保证安全。

答案 1 :(得分:22)

如果我没记错的话,在MYSQL COUNT(*)中计算所有行,而COUNT(column_name)只计算给定列中具有非NULL值的行。

答案 2 :(得分:11)

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

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

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

答案 3 :(得分:8)

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

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

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

显然,在mysql中它们没有区别,就像sqlserver一样,解析器似乎将查询更改为select(1)。对不起,如果我以某种方式误导你。

答案 4 :(得分:5)

我自己很好奇。阅读文献和理论答案都很好,但我喜欢用经验证据来平衡这些。

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

我在运行测试中看到的唯一其他可能的故障是缓存。第一次运行查询总是比使用相同索引的后续查询慢。我将在下面将其称为缓存Seeding调用。只是为了把它混合一点我用一个where子句运行它,我知道无论任何数据都会评估为真(TRUE = TRUE)。

这就是我的结果:

QueryType

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

COUNT()

      |  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(Id)的

      |  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

++这被认为是缓存Seeding调用。预计比其他人慢。

我说结果不言而喻。 COUNT(Id)通常会与其他人区分开来。添加Where子句会大大减少访问时间,即使它是您知道将评估为true的子句。最佳位置似乎是COUNT(Id)... WHERE Id IS NOT NULL。

我希望看到其他人的结果,也许是用较小的牌桌或者用不同领域的条款而不是你所计算的领域。我确定还有其他变化我没有考虑过。

答案 5 :(得分:4)

寻求替代方案

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

知道COUNT查询会变慢,您可以考虑另一种显示分页控件的方法,这种方法只允许您轻松查询慢速查询。谷歌的分页是一个很好的例子。

非规范化

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

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

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

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

根据您的体系结构,使用像memcached这样的缓存层来存储,递增和递减非规范化值可能是有意义的,并且当缓存键丢失时,简单地进入缓慢的COUNT查询。如果您的数据非常不稳定,这可以减少整体写入争用,但在这种情况下,您需要考虑solutions to the dog-pile effect

答案 6 :(得分:2)

MySQL ISAM表应该优化COUNT(*),跳过全表扫描。

答案 7 :(得分:2)

COUNT中的星号与星号无关,用于选择表格的所有字段。说COUNT(*)慢于COUNT(字段)

是纯粹的垃圾

我觉得选择COUNT(*)比选择COUNT(字段)快。如果RDBMS检测到您在COUNT而不是字段上指定“*”,则不需要评估任何增加计数的内容。而如果您在COUNT上指定字段,则RDBMS将始终评估您的字段是否为空以进行计数。

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

答案 8 :(得分:2)

COUNT(*)个事实和神话:

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

如果您有WHERE子句,大多数count(*)查询都会以相同的方式执行,否则您的InnoDB将不得不执行全表扫描。

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

答案 9 :(得分:2)

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

SELECT COUNT(`group_id`) FROM `group_relations`

答案 10 :(得分:1)

这应该取决于塞巴斯蒂安已经说过你实际想要实现的目标,即明确你的意图!如果 只计算行数,则转到COUNT(*),或计算单个列转到COUNT(列)。

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

答案 11 :(得分:1)

  

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

COUNT(1)曾经比COUNT(*)更快,但这不再是真的,因为现代DBMS足够聪明,知道你不想知道列

答案 12 :(得分:1)

我从MySQL那里得到的关于这样的事情的建议是,一般来说,尝试基于这样的技巧来优化查询从长远来看可能是一个诅咒。有一些关于MySQL历史的例子,其中依赖于优化器如何工作的某人的高性能技术最终成为下一版本的瓶颈。

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

也就是说,您可以在查询中执行一些操作以使优化程序更容易加快速度,但我不相信COUNT就是其中之一。

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

答案 13 :(得分:0)

  

我知道这通常是个坏主意   像这样的查询:

SELECT * FROM `group_relations`
     

但是,当我想要计数时,应该   我允许这样查询   要改变但仍然屈服的表格   相同的结果。

SELECT COUNT(*) FROM `group_relations`

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