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

时间:2010-04-26 01:10:50

标签: sql select count

我经常发现这三种变体:

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

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

6 个答案:

答案 0 :(得分:204)

底线

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

简而言之,不要将COUNT(1)用于任何事情。这是一个单一的小马,很少能满足你的需要,在极少数情况下相当于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连接,因为即使从属表与父表中的任何内容都不匹配,它也将返回1

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

不要被那些建议在COUNT中使用*的人愚弄,它从你的表中取出整行,说*很慢。 *SELECT COUNT(*)上的SELECT *对彼此没有影响,它们完全不同,只是共享一个共同的令牌,即*

替代语法

实际上,如果不允许将字段命名为与其表名相同,则RDBMS语言设计者可以为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
)

以null计数

而且,如果字段的名称与表名匹配,则使字段为空可能不是一个好习惯。假设你在fruit字段上有'Banana','Apple',NULL,'Pears'的值。这不会计算所有行,它只会产生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

仔细考虑,无论联接类型如何,您都可以推断COUNT(1)COUNT(*)相同。但是对于LEFT JOINs结果,我们无法将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

答案 1 :(得分:48)

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

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

假设pk是主键,并且值中不允许空值,则

  • COUNT(pk)也会计算行数

但是,如果pk不限制为非空,那么它会产生不同的答案:

  • COUNT(possibly_null)计算possibly_null列中包含非空值的行数。

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

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

  • COUNT(DISTINCT possibly_duplicated)计算possibly_duplicated列中NOT NULL列中不同(必然非空)值的数量。

通常,我会写COUNT(*);它是SQL的原始推荐表示法。同样,使用EXISTS子句,我通常会写WHERE EXISTS(SELECT * FROM ...),因为这是最初的推荐符号。替代品应该没有任何好处;优化器应该看到更加模糊的符号。

答案 2 :(得分:9)

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

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

答案 3 :(得分:6)

Asked and answered before...

Books on line说“COUNT ( { [ [ ALL | DISTINCT ] expression ] | * } )

“1”是非空表达式,因此它与COUNT(*)相同。 优化程序将其识别为 琐碎 ,因此提供相同的计划。 PK是唯一且非空(至少在SQL Server中),因此COUNT(PK) = COUNT(*)

这与EXISTS (SELECT * ...EXISTS (SELECT 1 ...

类似

请参阅ANSI 92 spec,第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.

答案 4 :(得分:5)

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

答案 5 :(得分:-1)

我觉得性能特征从DBMS变为DBMS。一切都取决于他们如何选择实施它。由于我在oracle上广泛工作,我从这个角度讲述。

COUNT(*) - 将整行提取到结果集中以便传递给count函数,如果该行不为null,则count函数将聚合为1

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

Count(PK) - oracle中的PK被编入索引。这意味着Oracle必须只读取索引。通常,索引B +树中的一行比实际行小许多倍。因此,考虑到磁盘IOPS速率,与整行相比,Oracle可以通过单个块传输从Index获取多倍的行。这会导致查询的吞吐量增加。

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