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

zne*_*eak 214 sql select count

我经常发现这三种变体:

SELECT COUNT(*) FROM Foo;
SELECT COUNT(1) FROM Foo;
SELECT COUNT(PrimaryKey) FROM Foo;
Run Code Online (Sandbox Code Playgroud)

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

Mic*_*uen 213

底线

使用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
Run Code Online (Sandbox Code Playgroud)

但是不要COUNT(*)用于LEFT连接,因为即使从属表与父表中的任何内容都不匹配,它也将返回1

SELECT boss.boss_id, COUNT(*)
FROM boss
LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
GROUP BY boss.id
Run Code Online (Sandbox Code Playgroud)

不要被那些建议*在COUNT中使用的人愚弄,它会从你的桌子中取出整行,说这*很慢.该*SELECT COUNT(*)SELECT *无轴承对方,他们是完全不同的事情,他们只是共用一个道理,即*.

另一种语法

实际上,如果不允许将字段命名为与其表名相同,则RDBMS语言设计器可以提供COUNT(tableNameHere)与其相同的语义COUNT(*).例:

对于行计数,我们可以这样:

SELECT COUNT(emp) FROM emp
Run Code Online (Sandbox Code Playgroud)

他们可以使它更简单:

SELECT COUNT() FROM emp
Run Code Online (Sandbox Code Playgroud)

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

SELECT boss.boss_id, COUNT(subordinate)
FROM boss
LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
GROUP BY boss.id
Run Code Online (Sandbox Code Playgroud)

但是他们不能这样做(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
)
Run Code Online (Sandbox Code Playgroud)

用null计数

而且,如果字段的名称与表名匹配,则使字段为空可能不是一个好习惯.假设你在fruit场上有'Banana','Apple',NULL,'Pears'的价值观.这不会计算所有行,它只会产生3而不是4

SELECT count(fruit) FROM fruit
Run Code Online (Sandbox Code Playgroud)

虽然有些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
Run Code Online (Sandbox Code Playgroud)

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

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

SELECT boss.boss_id, COUNT(subordinate.*)
FROM boss
LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
GROUP BY boss.id
Run Code Online (Sandbox Code Playgroud)

count(1):一招小马

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

SELECT COUNT(1) FROM tbl
Run Code Online (Sandbox Code Playgroud)

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

-- 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
Run Code Online (Sandbox Code Playgroud)

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

SELECT boss.boss_id, COUNT(1)
FROM boss
LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
GROUP BY boss.id
Run Code Online (Sandbox Code Playgroud)

这是......?

-- 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
Run Code Online (Sandbox Code Playgroud)

或这个...?

-- 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
Run Code Online (Sandbox Code Playgroud)

仔细考虑,无论连接类型如何,都可以推断出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
Run Code Online (Sandbox Code Playgroud)

适用于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
Run Code Online (Sandbox Code Playgroud)

计算集合基数的另一种方法,非常类似英语(只是不要创建一个名称与其表名相同的列):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
Run Code Online (Sandbox Code Playgroud)

你不能这样做: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
Run Code Online (Sandbox Code Playgroud)

你可以这样做,但这会产生错误的结果: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
Run Code Online (Sandbox Code Playgroud)

  • 或者从jester程序员那里,他们可以这样做:`SELECT COUNT('ME IN')FROM tbl`,因为认为像`COUNT(1)`中的1,'ME IN'将被RDBMS忽略和优化 (4认同)
  • COUNT(1)看起来像一个神奇的数字,当有人已经掌握了引擎盖下发生的事情时,就会使用它.它可能导致滥用(即,如果有恶意),因为所有COUNT(0),COUNT(1),COUNT(2),COUNT(42)(你得到的要点)都与COUNT相同(`*`),有人可能会混淆代码并使用COUNT(2),所以下一个维护者可能很难推断出这些COUNT的作用.当他/她已经收集COUNT(1)与COUNT(`*`)相同时,有人才会开始使用COUNT(1).没人用COUNT开始他们的数据库生涯(1) (3认同)
  • 当然它"确实有用",问题是否正常****?如果John有两个下属George和Ringo,而Paul没有,请尝试将`COUNT(1)`设置为`LEFT JOIN`它将正常工作,所以Paul的下属数将为0.首先解决这个问题:http !//www.sqlfiddle.com/# 1/98515/13 (2认同)
  • 我在关于在LEFT JOIN上使用`COUNT(1)`的回答中强调了这一声明:**您可以这样做,但这会产生错误的结果**。在此页面上搜索此短语:**错误的结果** (2认同)
  • 这个答案的第一句话就已经错了!*“使用 COUNT(field) 或 COUNT(*),并始终坚持使用”* 它们不是同一件事。`COUNT(*)` 和 `COUNT(1)` 是相同的,但是当你在那里放置一个非常量表达式时,它们就不同了。 (2认同)

Jon*_*ler 49

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

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

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

  • COUNT(pk) 还计算行数

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

  • 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 ...)因为这是最初的推荐符号.替代品应该没有任何好处; 优化器应该看到更加模糊的符号.

  • 我什至不知道 `COUNT(DISTINCT)` 工作,虽然它是有道理的。它是特定于 SQL 风格的,还是得到广泛支持? (2认同)
  • @zneak:COUNT(DISTINCT x) 自 SQL-86(第一个标准)以来一直在 SQL 中使用,所以我会惊讶地发现任何不支持它的 SQL DBMS。 (2认同)

Jar*_*ott 9

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

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

  • 有趣.您可以链接到的问题是否有任何基准? (5认同)

gbn*_*gbn 6

之前被问及回答......

在线图书说" COUNT ( { [ [ ALL | DISTINCT ] expression ] | * } )"

"1"是非空表达式,因此它与...相同COUNT(*).优化器认为它是微不足道的,因此给出了相同的计划.PK是唯一且非null(至少在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.
Run Code Online (Sandbox Code Playgroud)


Zei*_*ssS 5

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