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)
而且,如果字段的名称与表名匹配,则使字段为空可能不是一个好习惯.假设你在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)
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 ...)
因为这是最初的推荐符号.替代品应该没有任何好处; 优化器应该看到更加模糊的符号.
这取决于您使用的数据库类型以及某些情况下的表类型.
例如,使用MySQL,count(*)
在MyISAM表下会很快,但在InnoDB下会很慢.在InnoDB下你应该使用count(1)
或count(pk)
.
在线图书说" 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)