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中最快的。