我经常发现以下三种变体:

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

据我所知,它们都做同样的事情,我发现自己在代码库中使用了这三个。然而,我不喜欢用不同的方法做同一件事。我应该坚持哪一个?他们中有谁比另外两个好吗?


当前回答

其中两个总是得到相同的答案:

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

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

COUNT(pk)也计算行数

然而,如果pk没有被约束为非空,那么它会产生一个不同的答案:

COUNT(possiby_null)计数在possiby_null列中具有非空值的行数。 COUNT(DISTINCT pk)也计算行数(因为主键不允许重复)。 COUNT(DISTINCT possibly_null_or_dup)计数列possibly_null_or_dup中不同的非空值的数量。 COUNT(DISTINCT possiby_duplication)计数当possiby_duplication列上有NOT NULL子句时,该列中不同(必然非NULL)值的数量。

通常,我写COUNT(*);它是SQL最初推荐的符号。类似地,对于EXISTS子句,我通常写WHERE EXISTS(SELECT * FROM…),因为这是最初的推荐符号。替代方案应该没有好处;优化器应该能看穿更模糊的符号。

其他回答

我感觉不同DBMS的性能特征是不同的。这完全取决于他们选择如何实施它。因为我在甲骨文工作过很长时间,所以我将从这个角度来讲述。

COUNT(*) -在传递给COUNT函数之前将整行获取到结果集中,如果行不为空,COUNT函数将聚合1

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

COUNT(PK) - Oracle中的PK被索引。这意味着Oracle只能读取索引。通常,索引B+树中的一行比实际行小很多倍。因此,考虑到磁盘IOPS速率,Oracle通过单个块传输可以从Index中获取比整行多很多倍的行。这将导致更高的查询吞吐量。

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

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

之前的提问和回答…

书在网上说“计数({[[所有|独特]表达式]| *})”

“1”是一个非空表达式,因此它与COUNT(*)相同。 优化器认为它是微不足道的,所以给出相同的计划。一个PK是唯一且非空的(至少在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.

其中两个总是得到相同的答案:

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

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

COUNT(pk)也计算行数

然而,如果pk没有被约束为非空,那么它会产生一个不同的答案:

COUNT(possiby_null)计数在possiby_null列中具有非空值的行数。 COUNT(DISTINCT pk)也计算行数(因为主键不允许重复)。 COUNT(DISTINCT possibly_null_or_dup)计数列possibly_null_or_dup中不同的非空值的数量。 COUNT(DISTINCT possiby_duplication)计数当possiby_duplication列上有NOT NULL子句时,该列中不同(必然非NULL)值的数量。

通常,我写COUNT(*);它是SQL最初推荐的符号。类似地,对于EXISTS子句,我通常写WHERE EXISTS(SELECT * FROM…),因为这是最初的推荐符号。替代方案应该没有好处;优化器应该能看穿更模糊的符号。

底线

使用COUNT(字段)或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

但是不要对LEFT连接使用COUNT(*),因为即使从属表不匹配父表中的任何内容,它也会返回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, '梨'在水果领域。这不会计算所有行,它只会产生3行,而不是4行

SELECT count(fruit) FROM fruit

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

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

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

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

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

只会一招的小马

特别是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(sub .boss_id), COUNT(sub .*)

所以你可以使用以下任何一种方法:

-- 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