代码之家  ›  专栏  ›  技术社区  ›  grapefrukt

我应该数数(*)吗?

  •  75
  • grapefrukt  · 技术社区  · 16 年前

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

    SELECT * FROM `group_relations`
    

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

    SELECT COUNT(*) FROM `group_relations`
    

    或者更具体

    SELECT COUNT(`group_id`) FROM `group_relations`
    

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

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

    14 回复  |  直到 16 年前
        1
  •  106
  •   Iain Holder    16 年前

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

    select count(*)
    

    将计算所有行,而

    select count(group_id)
    

    将只计算group_id不为空的行。

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

    就我个人而言,在计数时,我做count(*)是为了安全起见。

        2
  •  22
  •   Sebastian Dietz    16 年前

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

        3
  •  11
  •   Eran Galperin    16 年前

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

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

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

        4
  •  7
  •   Sergio    16 年前

    如果你尝试 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)。对不起,如果我以某种方式误导了你。

        5
  •  5
  •   Chris    16 年前

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

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

    我在运行测试时看到的唯一其他可能的故障是缓存。第一次运行查询总是比使用相同索引的后续查询慢。我将在下面将其称为缓存种子调用。为了稍微混淆一下,我用where子句运行了它,我知道不管任何数据如何,where子句的计算结果总是为真(true=true)。

    这就是我的结果:

    查询类型

          |  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
    

    计数(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
    

    计数(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子句来表示与你所计算的字段不同的字段。我确信还有其他变化我没有考虑到。

        6
  •  4
  •   Duncan Beevers    16 年前

    寻找替代方案

    如您所见,当桌子变大时, 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 .

        7
  •  2
  •   dmajkic    16 年前

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

        8
  •  2
  •   Michael Buen    16 年前

    COUNT中的星号与星号没有关系,用于选择表的所有字段。说COUNT(*)比COUNT(field)慢纯粹是无稽之谈

    我直觉地认为,选择COUNT(*)比选择COUNT(字段)更快。如果RDBMS检测到您在COUNT而不是字段上指定了“*”,则不需要计算任何值来增加计数。然而,如果您在COUNT上指定字段,RDBMS将始终评估您的字段是否为空,以对其进行计数。

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

        9
  •  2
  •   Charles Faiga    16 年前

    计数(*)事实和神话:

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

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

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

        10
  •  2
  •   pjau    16 年前

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

    SELECT COUNT(`group_id`) FROM `group_relations`
    
        11
  •  1
  •   tddmonkey    16 年前

    正如塞巴斯蒂安所说,这应该取决于你真正想要实现的目标,即明确你的意图!如果你 只计算行数就可以得到COUNT(*),或者计算一列就可以得到COUNT(列)。

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

        12
  •  1
  •   Rafael Mueller    16 年前

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

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

        13
  •  1
  •   Jon    16 年前

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

    编写一个查询来回答你要问的问题——如果你想计算所有行的数量,请使用count(*)。如果你想要非空列的计数,请使用count(col),其中col不是null。适当地索引,并将优化留给优化器。试图进行自己的查询级优化有时会降低内置优化器的效率。

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

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

        14
  •  0
  •   Carl Manaster    16 年前

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

    SELECT * FROM `group_relations`
    

    但当我只想要计数时,应该 我选择这个问题,因为这允许 桌子会变,但仍然会让步 相同的结果。

    SELECT COUNT(*) FROM `group_relations`
    

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

    推荐文章