我应该(*)还是不?
我知道做这样的查询通常是一个坏主意:
SELECT * FROM `group_relations`
但是,当我只是想要计数,我应该去这个查询,因为这允许表更改,但仍产生相同的结果。
SELECT COUNT(*) FROM `group_relations`
或者更具体的
SELECT COUNT(`group_id`) FROM `group_relations`
我有一种感觉,后者可能会更快,但还有其他的事情要考虑吗?
更新 :我在这种情况下使用InnoDB,抱歉没有更具体。
如果所讨论的列不是NULL,那么你的两个查询都是等价的。 当group_id包含空值时,
select count(*)
将统计所有行,而
select count(group_id)
将只计算group_id不为null的行。
另外,有些数据库系统(如MySQL)在请求count(*)时使用了优化,这使得查询比特定查询快一些。
就个人而言,当我正在计数时,我正在计数(*)与空值安全的一面。
如果我记得是正确的,在MYSQL COUNT(*)计数所有行,而COUNT(column_name)只计算给定列中具有非NULL值的行。
COUNT(*)计算所有行,而COUNT(column_name)只计算指定列中没有NULL值的行。
在MySQL中需要注意的一点是:
由于行计数被caching,因此COUNT()在MyISAM表上对于非空列是非常快的。 InnoDB没有行计数caching,所以COUNT(*)或COUNT(column_name)的性能没有差别,无论列是否为空。 你可以在MySQL性能博客上阅读这篇文章的不同之处。
如果您尝试SELECT COUNT(1) FROM
group_relations,它会快一点,因为它不会尝试从您的列中检索信息。
编辑:我只是做了一些研究,发现这只发生在一些分贝。 在sqlserver中使用1或*是相同的,但在oracle上使用1更快。
显然他们之间没有任何区别,像sqlserverparsing器似乎改变查询select(1)。 对不起,如果我以某种方式误导你。
我自己对此很好奇。 阅读文档和理论答案都很好,但我喜欢用经validation据来平衡这些问题。
我有一个MySQL表(InnoDB)有5,607,997条logging。 该表在我自己的私人沙盒,所以我知道内容是静态的,没有人正在使用服务器。 我认为这有效地消除了所有外部影响的performance。 我有一个auto_increment主键字段(Id),我知道永远不会为null,我将用于我的where子句testing(WHERE ID是不是NULL)的表。
我在运行testing中看到的唯一其他可能的故障是caching。 第一次运行查询总是比使用相同索引的后续查询慢。 我将在下面将其称为caching播种呼叫。 只是把它混合一点,我运行一个where子句,我知道总是会评估为true,而不pipe任何数据(TRUE = TRUE)。
这就是说我的结果:
查询types
| 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
++这被认为是高速caching播种呼叫。 预计会比其他的要慢。
我想说的结果说明一切。 COUNT(Id)通常会将其他内容排除在外。 添加一个Where子句可以显着减less访问时间,即使它是一个你知道将评估为真的子句。 甜蜜点似乎是COUNT(Id)…在哪里Id不是NULL。
我希望看到其他人的结果,也许用更小的表格或者与除了你要计算的字段不同的字段的where子句。 我相信还有其他的变化,我没有考虑到。
寻求替代品
正如你所看到的,当表变大时, COUNT
查询变慢。 我认为最重要的是考虑你正在努力解决的问题的性质。 例如,许多开发人员在为大量logging生成分页时使用COUNT
查询来确定结果集中的页面总数。
知道COUNT
查询的速度会变慢,您可以考虑另一种显示分页控件的方法,它只是简单地让您慢速查询。 Google的分页就是一个很好的例子。
非规范化
如果您绝对必须知道与特定计数匹配的logging数量,请考虑经典的数据非规范化技术。 不要在查找时计算行数,而应考虑在logging插入时增加计数器,并在删除logging时递减计数器。
如果您决定这样做,请考虑使用幂等事务操作来保持非规范化的值同步。
BEGIN TRANSACTION; INSERT INTO `group_relations` (`group_id`) VALUES (1); UPDATE `group_relations_count` SET `count` = `count` + 1; COMMIT;
或者,如果RDBMS支持它们,则可以使用数据库触发器。
根据您的体系结构,使用像memcached这样的caching层来存储,递增和递减非规格化的值可能是有意义的,当caching键丢失时,简单地通过缓慢的COUNT查询。 如果您的数据非常不稳定,这可以减less整个写入争用,但是在这种情况下,您将需要考虑解决方法 。
MySQL ISAM表应该对COUNT(*)进行优化,跳过全表扫描。
COUNT中的星号不带星号,用于select表格的所有字段。 说COUNT(*)慢于COUNT(字段)是纯粹的垃圾,
我直觉selectCOUNT(*)比selectCOUNT(字段)更快。 如果RDBMS检测到在COUNT而不是字段上指定了“*”,则不需要评估任何值来增加计数。 而如果您在COUNT上指定了字段,则RDBMS将始终评估您的字段是否为空或不计数。
但是,如果您的字段可以为空,请在COUNT中指定该字段。
COUNT(*)事实和神话:
误区 :“InnoDB不能很好地处理(*)查询”:
如果你有一个WHERE子句,大多数count(*)查询的执行方式与所有存储引擎相同,否则InnoDB将不得不执行全表扫描。
事实 :InnoDB不会在没有where子句的情况下优化count(*)查询
最好通过索引列(如主键)进行计数。
SELECT COUNT(`group_id`) FROM `group_relations`
这应该取决于塞巴斯蒂安已经说过的你实际上想要达到什么目的,即明确你的意图! 如果您只是对行进行计数,然后进行COUNT(*)计数,或者对COUNT(列)进行计数。
也许值得查看你的数据库供应商。 当我以前使用Informix时,它对COUNT(*)进行了优化,查询计划的执行成本为1,而对单列或多列进行计数则会导致更高的数字
如果您尝试SELECT COUNT(1)FROM group_relations,它会快一点,因为它不会尝试从您的列中检索信息。
COUNT(1)以前比COUNT(*)要快,但事实并非如此,因为现代DBMS足够聪明,知道你不想知道列
我从MySQL那里得到的build议是,一般来说,基于这样的技巧来优化查询从长远来看可能是一个诅咒。 有一些关于MySQL历史的例子,其中依赖于优化器工作原理的高性能技术最终成为下一版本的瓶颈。
编写回答你所问的问题的查询 – 如果你想要所有行的计数,使用COUNT(*)。 如果需要非空列的计数,请使用COUNT(col)WHERE col IS NOT NULL。 适当地索引,并将优化留给优化器。 试图使自己的查询级优化有时可以使内置的优化器效率较低。
也就是说,在查询中您可以执行某些操作,以便优化程序加快速度,但是我不认为COUNT就是其中之一。
编辑:虽然上面的答案统计是有趣的。 在这种情况下,我不确定在优化器中是否有实际的工作。 一般来说,我只是在讨论查询级优化。
我知道做这样的查询通常是一个坏主意:
SELECT * FROM `group_relations`
但是,当我只是想要计数,我应该去这个查询,因为这允许表更改,但仍产生相同的结果。
SELECT COUNT(*) FROM `group_relations`
正如你的问题所暗示的那样, SELECT *
不明智的原因是对表的改变可能需要你的代码改变。 这不适用于COUNT(*)
。 想要SELECT COUNT('group_id')
给你的专门行为 – 通常你想知道logging的数量是非常罕见的。 这就是COUNT(*)
的用途,所以使用它。