COUNT(*)与COUNT(1)与COUNT(pk):哪个更好?

我经常发现这三个变种:

SELECT COUNT(*) FROM Foo; SELECT COUNT(1) FROM Foo; SELECT COUNT(PrimaryKey) FROM Foo; 

据我所见,他们都做同样的事情,我发现自己在我的代码库中使用三个。 但是,我不喜欢以不同的方式做同样的事情。 我应该坚持哪一个? 他们中的任何一个比另外两个好?

底线

使用COUNT(field)COUNT(*) ,并坚持使用它,如果您的数据库允许COUNT(tableHere)COUNT(tableHere.*) ,使用它。

总之,不要使用COUNT(1) 。 这是一个小把戏,很less做你想要的,在这些罕见的情况下相当于count(*)

使用count(*)进行计数

使用*为所有需要计数的所有查询,即使对于连接,使用*

 SELECT boss.boss_id, COUNT(subordinate.*) FROM boss LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id GROUP BY boss.id 

但不要使用COUNT(*)作为LEFT连接,因为即使从属表不匹配父表中的任何内容

 SELECT boss.boss_id, COUNT(*) FROM boss LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id GROUP BY boss.id 

不要被那些build议当在COUNT中使用*时候,从表中取整行,说*很慢。 在SELECT COUNT(*)SELECT *对彼此没有影响,它们是完全不同的东西,它们只是共享一个共同的标记,即*

另一种语法

实际上,如果不允许将字段命名为与表名相同的字段,那么RDBMS语言devise者可以给COUNT(tableNameHere)赋予与COUNT(*)相同的语义。 例:

为了计算行,我们可以这样做:

 SELECT COUNT(emp) FROM emp 

而且他们可以简化一下:

 SELECT COUNT() FROM emp 

对于LEFT JOIN,我们可以这样做:

 SELECT boss.boss_id, COUNT(subordinate) FROM boss LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id GROUP BY boss.id 

但是他们不能这么做( COUNT(tableNameHere) ),因为SQL标准允许用与其表名相同的名字来命名一个字段:

 CREATE TABLE fruit -- ORM-friendly name ( fruit_id int NOT NULL, fruit varchar(50), /* same name as table name, and let's say, someone forgot to put NOT NULL */ shape varchar(50) NOT NULL, color varchar(50) NOT NULL ) 

用空值计数

而且,如果一个字段的名字与表名相匹配,那么这个字段不可空。 假设你在fruit领域有“香蕉”,“苹果”,NULL,“梨”的价值。 这将不计算所有的行,它只会产生3,而不是4

 SELECT count(fruit) FROM fruit 

虽然有些RDBMS做这样的原则(对表的行进行计数,它接受表名作为COUNT的参数),但这将在Postgresql中工作(如果在下面两个表中没有任何一个subordinate字段,即只要有字段名称和表名之间没有名称冲突):

 SELECT boss.boss_id, COUNT(subordinate) FROM boss LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id GROUP BY boss.id 

但是,如果我们在表中添加一个subordinate字段,那么这可能会导致混淆,因为它会计算字段(可以为空),而不是表行。

所以为了安全起见,请使用:

 SELECT boss.boss_id, COUNT(subordinate.*) FROM boss LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id GROUP BY boss.id 

count(1) :一招小马

特别是对于COUNT(1) ,这是一个一招的小马 ,它只适用于一个表的查询:

 SELECT COUNT(1) FROM tbl 

但是,当你使用连接时,这个技巧不会在多表查询中工作,而不会混淆它的语义,特别是你不能写:

 -- count the subordinates that belongs to boss SELECT boss.boss_id, COUNT(subordinate.1) FROM boss LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id GROUP BY boss.id 

那么COUNT(1)的含义是什么?

 SELECT boss.boss_id, COUNT(1) FROM boss LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id GROUP BY boss.id 

这是…吗?

 -- counting all the subordinates only SELECT boss.boss_id, COUNT(subordinate.boss_id) FROM boss LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id GROUP BY boss.id 

或这个…?

 -- or is that COUNT(1) will also count 1 for boss regardless if boss has a subordinate SELECT boss.boss_id, COUNT(*) FROM boss LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id GROUP BY boss.id 

通过仔细思考,无论连接的types如何,都可以推断COUNT(1)COUNT(*)相同。 但是对于LEFT JOIN的结果,我们不能将COUNT(1)化为: COUNT(subordinate.boss_id)COUNT(subordinate.*)

所以只需使用以下任一项:

 -- count the subordinates that belongs to boss SELECT boss.boss_id, COUNT(subordinate.boss_id) FROM boss LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id GROUP BY boss.id 

在Postgresql上工作,显然你要计算集合的基数

 -- count the subordinates that belongs to boss SELECT boss.boss_id, COUNT(subordinate.*) FROM boss LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id GROUP BY boss.id 

另一种方法来计算集合的基数,非常类似于英文(只是不要使用与表名相同的名称): http : //www.sqlfiddle.com/#!1/98515/7

 select boss.boss_name, count(subordinate) from boss left join subordinate on subordinate.boss_code = boss.boss_code group by boss.boss_name 

你不能这样做: http : //www.sqlfiddle.com/#!1/98515/8

 select boss.boss_name, count(subordinate.1) from boss left join subordinate on subordinate.boss_code = boss.boss_code group by boss.boss_name 

你可以这样做,但这会产生错误的结果: http : //www.sqlfiddle.com/#!1/98515/9

 select boss.boss_name, count(1) from boss left join subordinate on subordinate.boss_code = boss.boss_code group by boss.boss_name 

其中两个总是产生相同的答案:

  • COUNT(*)计算行数
  • COUNT(1)也计算行数

假设' pk '是一个主键,并且值中不允许有空值,那么

  • COUNT(pk)也计算行数

但是,如果“ pk ”不被限制为非空,则会产生不同的答案:

  • COUNT(possible_null)计算may_null列中具有非空值的行数。

  • COUNT(DISTINCT pk)也计算行数(因为主键不允许重复)。

  • COUNT(DISTINCT possible_null_or_dup)计算列possibly_null_or_dup中不同的非空值的数量。

  • COUNT(DISTINCT might_duplicated)在有NOT NULL子句的情况下对possibly_duplicated的列中的值进行计数(必须为非空值)。

通常,我写COUNT(*) ; 它是SQL的原始推荐符号。 同样,对于EXISTS子句,我通常会编写WHERE EXISTS(SELECT * FROM ...)因为这是原来的推荐符号。 替代scheme应该没有好处; 优化器应该看穿更晦涩的符号。

这将取决于您使用的数据库types以及某些情况下的表types。

例如,使用MySQL, count(*)将在MyISAM表下快速,但在InnoDB下会很慢。 在InnoDB下,您应该使用count(1)count(pk)

至less在Oracle上它们都是一样的: http : //www.oracledba.co.uk/tips/count_speed.htm

问及之前回答…

在线书籍显示 “ COUNT ( { [ [ ALL | DISTINCT ] expression ] | * } )

“1”是一个非空的expression式,因此它与COUNT(*)相同。 优化者认识到它是微不足道的,所以给出了相同的计划。 一个PK是唯一的,非空(至less在SQL Server中),所以COUNT(PK) = COUNT(*)

这是一个类似的神话EXISTS (SELECT * ...EXISTS (SELECT 1 ...

请参阅ANSI 92规范第6.5节“通用规则”第1种情况

  a) If COUNT(*) is specified, then the result is the cardinality of T. b) Otherwise, let TX be the single-column table that is the result of applying the <value expression> to each row of T and eliminating null values. If one or more null values are eliminated, then a completion condition is raised: warning- null value eliminated in set function. 

我觉得从DBMS到DBMS的性能特征发生了变化。 它的一切就如何select暗示它。 由于我曾经在oracle上做过广泛的工作,所以从这个angular度来讲,

COUNT(*) – 将整行读入到计数函数结果集中,如果行不为null,count函数将聚合1

COUNT(1) – 不会获取任何行,而是在where匹配时,对表中的每一行调用count为1的常量值。

Count(PK) – oracle中的PK被索引。 这意味着Oracle必须只读取索引。 通常索引B +树中的一行比实际行小很多倍。 所以考虑到磁盘IOPS率,Oracle可以通过一个块传输从索引中获取更多的行,而不是整行。 这导致更高的查询吞吐量。

从这里你可以看到第一个计数是最慢的,最后一个计数是Oracle中最快的。