75

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

SELECT * FROM `group_relations`

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

SELECT COUNT(*) FROM `group_relations`

或者更具体的

SELECT COUNT(`group_id`) FROM `group_relations`

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

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

4

14 回答 14

108

如果有问题的列不为空,则您的两个查询都是等效的。当 group_id 包含空值时,

select count(*)

将计算所有行,而

select count(group_id)

只会计算 group_id 不为空的行。

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

就个人而言,当只是数数时,我正在做 count(*) 以保证空值的安全。

于 2009-01-19T11:12:40.183 回答
22

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

于 2009-01-19T11:10:50.950 回答
11

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

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

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

于 2009-01-19T11:32:24.650 回答
7

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

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

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

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

于 2009-01-19T11:14:41.317 回答
5

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

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

我在运行测试中看到的唯一其他可能的故障是缓存。第一次运行查询总是比使用相同索引的后续查询慢。我将在下面将其称为缓存播种调用。只是为了把它混在一起,我用一个 where 子句运行它,我知道无论任何数据如何(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

计数(编号)

      |  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

计数(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)...其中 Id 不为空。

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

于 2009-03-19T21:36:00.463 回答
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 查询。如果您有非常不稳定的数据,这可以减少整体写入争用,但在这种情况下,您需要考虑解决狗桩效应的方法

于 2009-07-08T06:40:54.680 回答
2

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

于 2009-01-19T11:39:31.860 回答
2

COUNT 中的星号与用于选择表的所有字段的星号无关。说 COUNT(*) 比 COUNT(field) 慢纯属胡说八道

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

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

于 2009-01-19T12:00:07.480 回答
2

COUNT(*) 事实和神话:

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

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

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

于 2009-03-09T20:56:11.173 回答
2

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

SELECT COUNT(`group_id`) FROM `group_relations`
于 2009-06-22T22:49:55.240 回答
1

正如塞巴斯蒂安已经说过的那样,这应该取决于您实际想要实现的目标,即明确您的意图!如果您只是计算行数,则选择 COUNT(*),或者计算单个列,则选择 COUNT(column)。

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

于 2009-01-19T11:14:51.003 回答
1

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

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

于 2009-01-19T11:44:07.717 回答
1

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

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

也就是说,您可以在查询中做一些事情来使优化器更容易加速它,但我不相信 COUNT 是其中之一。

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

于 2009-03-27T19:28:37.693 回答
0

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

SELECT * FROM `group_relations`

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

SELECT COUNT(*) FROM `group_relations`

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

于 2009-06-22T23:01:59.943 回答