近期浏览网页时又看到类似内容的文章,遂觉得有必要写一篇文章深入分析一下该问题的历史渊源,何以持续探讨了几十年。
对于初级开发者、DBA、数据开发人员来说,面试时很可能会被问到“COUNT(*)和COUNT(1)有什么区别?”这个问题。网上也有很多资料有说不一样的(早年间很普遍),有说一样的。那么真实情况是什么样呢?下面且听我娓娓道来。
正常来讲,一个设计良好、符合国际标准的数据库管理软件系统,COUNT(1)
与 COUNT(*)
是完全一样的,甚至 Oracle 著名专家 Tom 回答说建议使用 COUNT(*)
,因为 COUNT(1)
在 Oracle 内部解析后会转化为 COUNT(*)
。
那么为什么又会有这个问题呢?而且问得很频繁,又持续了这么多年?答案无外乎两个原因:设计问题、错误的官方文档。
SQL-92 标准、SQL-99 标准 :
a) If COUNT(*) is specified, then the result is the cardinality of T.
可见 COUNT(*)
的结果就是所统计表的基数或势(cardinality )。
SQL-92 标准、SQL-99 标准 对于集合函数声明的定义如下:
6.5
Function
Specify a value derived by the application of a function to an
argument.
Format
::=
COUNT
|
::=
[ ]
::=
AVG | MAX | MIN | SUM | COUNT
::= DISTINCT | ALL
由于 SQL 中 *
为特殊字符,拥有特殊含义,所以导致一些人不必要地对 COUNT()
函数也特殊对待了。
COUNT(1)
其实是 COUNT(表达式)
,只不过此处是常量表达式 1
。实际上,COUNT(*)
也是 COUNT(表达式)
。二者是一样的。
在查阅 Ask Tom 时,会发现讨论得很有意思,跟中文互联网圈的现状极其相似,有捧的,有抖机灵的“大聪明”,有跑题问别的问题的,有点名踩“大聪明”的。原来老外领先了一个版本啊!
Tom 回复如下:
nothing, they are the same, incur the same amount of work -- do the same thing, take the same amount of resources.
count(1) is just counter intuitive to me and is silently rewritten as count(*) internally...
简单说,COUNT(1)
与 COUNT(*)
二者完全一样,做相同的工作,消耗相同的资源。COUNT(1)
对我来说仅是反直觉的,在内部被静默地重写为 COUNT(*)
。
而另一位网友的回复则暴露了万恶之源是 OCP 的培训文档:
Helena Marková, August 14, 2003 - 8:14 am UTC
I have found another opinion
Sergej, October 20, 2003 - 4:51 pm UTC
Hi Tom,
There is another opinion in:
OCP Introduction to Oracle 9i: SQL Exam Guide, p. 124
"Do not use count(*) to determine the number of rows in a table. Use count(1) or count(ROWID) instead. These options are faster because they bypass some unnecessary operations in Oracle's SQL pocessing mechanism."
Thanks,
Best,
Sergej
其他网友则回复他指出 OCP 培训文档有错误并不稀奇,并又列举了一个错误。
至此,我们找到问题的根源了!原来是不专业的 OCP 文档编写人员犯的错误导致的!
笔者之前也考过 OCP,培训资料看了一些,确实有错误,而且背的700多道题目也有很多答案是错的!大约占总数的 10%。我当时怀疑是培训机构故意整成错误的答案,防止大批人员考满分。笔者记忆力还行,考前全部背了几遍,几乎全都背下来了。考完信心满满以为都能拿满分,结果并不是!错了一两道。
而且,笔者第一份工作从事的是 Oracle 数据库开发工作,当时老前辈传给我们的 SQL 编写经验就说 COUNT(1)
比 COUNT(*)
快,因为后者会查询数据字典获取表的字段信息。网上的博客园、CSDN、ITPUB 等网站上的文章也大多如此说,自己也没加多想,变信以为真了。等工作了几年后,发现这完全是反智言论!
好了,正式说完了,来说说搞笑的。
论坛中该帖子下面充满了对这位叫 mikito 网友的口诛笔伐。
来看个最直接的:
mikito - you’re an idiot
anthony, July 07, 2004 - 4:49 pm UTC
Honestly,
You impress exactly zero people.
You come to this site and cry about Oracle…
Cry about Oracle’s sql…
Cry about analytics…
那么他说了什么呢?
这大聪明说了如下内容:
count is sum(1)
Mikito Harakiri, September 04, 2001 - 12:45 am UTCSome extra info:
- There is no need in a separate “count” function as
select sum(1) from emp
does the job (and could do more;).
- “count” as an abbreviation for sum(1) doesn’t really need an argument, for example
select count(1) from emp
and
select count(2) from emp
return the same data.
In short, “count” having an argument is counterintuitive, at least.
聪明吧!不止如此,还有这个:
It’s ANSI SQL bug!
Mikito Harakiri, July 06, 2004 - 8:23 pm UTCIt’s just a misconception that “count” should have any arguments at all. Indeed when we count
int count = 0;
for( int i = 0; i< 10; i++)
count++;we use a single argument increment operator ++. Likewise, any “normal” aggregation
int sum = 0;
for( int i = 0; i< 10; i++)
sum = sum + element[i];uses 2 argument operator – “+”, “max”, “min”, etc. Therefore, we need one argument for normal aggregates, and no arguments for the count.
As far as
select count(distinct ename) from emp
is concerned, this odd syntax has been invented for retards who are unable to grasp what inner view is and figure out that
select count from (
select distinct empno from emp
)is much cleaner syntax.
看完这两个,你就理解喷他的国外喷子了!哈哈!
不乏一些数据库系统早期版本存在一些设计问题的可能,尤其是数据库理论刚刚成型、应用,SQL 标准尚未制定以前。这些设计问题导致出现二者不一样的问题。但如今但凡一个流行的满足 ANSI SQL-92 以上标准的数据库系统,都应该认为二者是完全等同的。