关于Count(*)vs Count(1):Count(*)vs Count(1) – SQL Server

Count(*) vs Count(1) - SQL Server

只是想知道你们中是否有人使用过Count(1)而不是Count(*),以及在性能上是否有明显的差异,或者这只是过去几天的遗留习惯?

(具体数据库为SQL Server 2005)


没有区别。

原因:

Books on-line says"COUNT ( { [ [ ALL | DISTINCT ] expression ] | * } )"

"1"是一个非空表达式:所以它与COUNT(*)相同。优化器识别出它是什么:微不足道的。

EXISTS (SELECT * ...EXISTS (SELECT 1 ...

例子:

1
2
3
4
5
SELECT COUNT(1) FROM dbo.tab800krows
SELECT COUNT(1),FKID FROM dbo.tab800krows GROUP BY FKID

SELECT COUNT(*) FROM dbo.tab800krows
SELECT COUNT(*),FKID FROM dbo.tab800krows GROUP BY FKID

相同的IO,相同的计划,工作

编辑,八月2011

关于dba.se的类似问题。

编辑,DEC 2011

在ansi-92中特别提到了COUNT(*)(参见"EDOCX1〔15》)。

Case:

a) If COUNT(*) is specified, then the result is the cardinality of T.

也就是说,美国国家标准协会的标准承认它是明显的流血什么意思。由于这种迷信,RDBMS供应商已经对COUNT(1)进行了优化。否则将按照ANSI进行评估。

b) Otherwise, let TX be the single-column table that is the
result of applying the to each row of T
and eliminating null values. If one or more null values are
eliminated, then a completion condition is raised: warning-


在SQL Server中,这些语句产生相同的计划。

与流行观点相反,在甲骨文中,他们也这么做。

Oracle中的SYS_GUID()是一个计算密集型函数。

在我的测试数据库中,t_even是一个带有1,000,000行的表。

此查询:

1
2
SELECT  COUNT(SYS_GUID())
FROM    t_even

运行48秒,因为函数需要评估返回的每个SYS_GUID(),以确保它不是NULL

但是,此查询:

1
2
3
4
5
SELECT  COUNT(*)
FROM    (
        SELECT  SYS_GUID()
        FROM    t_even
        )

只运行了2秒,因为它甚至没有尝试评估SYS_GUID()(尽管*COUNT(*)的论点)


显然,count(*)和count(1)将始终返回相同的结果。因此,如果一个比另一个慢,那实际上是由于一个乐观主义者的错误。由于这两种形式在查询中都非常频繁,DBMS不允许这样的错误保持不变。因此,您将发现这两种形式的性能(可能)在所有主要的SQL DBMS中都是相同的。


在SQL-92标准中,COUNT(*)特别表示"表表达式的基数"(可以是基表、视图、派生表、CTE等)。

我想这个想法是COUNT(*)很容易解析。使用任何其他表达式都需要解析器确保它不引用任何列(COUNT('a'),其中a是文本,COUNT(a),其中a是列,可以产生不同的结果)。

同样,熟悉SQL标准的人类编码人员也可以很容易地识别COUNT(*),这是在处理多个供应商的SQL产品时的一项有用技能。

另外,在特殊情况下,SELECT COUNT(*) FROM MyPersistedTable;认为DBMS可能保存表的基数统计信息。

因此,由于COUNT(1)COUNT(*)在语义上是等价的,所以我使用COUNT(*)


在结果和性能方面,COUNT(*)COUNT(1)是相同的。


我希望乐观主义者能确保在奇怪的边缘情况之外没有真正的区别。

与任何事情一样,唯一真正的方法是衡量你的具体情况。

也就是说,我一直使用COUNT(*)


我在8 GB RAM Hyper-V设备上对SQL Server 2012进行了快速测试。你可以自己看看结果。运行这些测试时,除了SQL Server Management Studio之外,我没有运行任何其他窗口应用程序。

我的表架构:

1
2
3
4
5
6
7
8
9
10
CREATE TABLE [dbo].[employee](
    [Id] [BIGINT] IDENTITY(1,1) NOT NULL,
    [Name] [nvarchar](50) NOT NULL,
 CONSTRAINT [PK_employee] PRIMARY KEY CLUSTERED
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

Employee表总记录数:178090131(约1.78亿行)

第一个查询:

1
2
3
4
5
6
SET Statistics TIME ON
GO    
SELECT COUNT(*) FROM Employee
GO    
SET Statistics TIME Off
GO

第一次查询的结果:

1
2
3
4
5
6
7
8
9
 SQL Server parse AND compile TIME:
 CPU TIME = 0 ms, elapsed TIME = 35 ms.

 (1 ROW(s) affected)

 SQL Server Execution Times:
   CPU TIME = 10766 ms,  elapsed TIME = 70265 ms.
 SQL Server parse AND compile TIME:
   CPU TIME = 0 ms, elapsed TIME = 0 ms.

第二个查询:

1
2
3
4
5
6
    SET Statistics TIME ON
    GO    
    SELECT COUNT(1) FROM Employee
    GO    
    SET Statistics TIME Off
    GO

第二次查询结果:

1
2
3
4
5
6
7
8
9
 SQL Server parse AND compile TIME:
   CPU TIME = 14 ms, elapsed TIME = 14 ms.

(1 ROW(s) affected)

 SQL Server Execution Times:
   CPU TIME = 11031 ms,  elapsed TIME = 70182 ms.
 SQL Server parse AND compile TIME:
   CPU TIME = 0 ms, elapsed TIME = 0 ms.

您可以注意到有83(=70265-70182)毫秒的差异,这很容易归因于运行查询时的确切系统条件。我也做了一次单次跑步,所以如果我做了几次跑步并做了一些平均值,这个差异会变得更精确。如果对于如此巨大的数据集,差异小于100毫秒,那么我们可以很容易地得出结论,这两个查询没有任何SQL Server引擎所显示的性能差异。

注意:在两次运行中,RAM命中率接近100%。在开始两次运行之前,我重新启动了SQL Server服务。


随着这个问题一次又一次的出现,这里还有一个答案。我希望为初学者添加一些关于"最佳实践"的内容。

SELECT COUNT(*) FROM something统计记录,这是一项简单的任务。

SELECT COUNT(1) FROM something为每条记录检索一个1,然后对不为空的1进行计数,这实质上是对记录进行计数,只是比较复杂。

这样说:好的DBMS注意到第二个语句将导致与第一个语句相同的计数,并相应地重新解释它,以免做不必要的工作。因此,通常这两个语句都会导致相同的执行计划,并占用相同的时间。

但是,从可读性的角度来看,您应该使用第一条语句。您想对记录进行计数,所以要对记录进行计数,而不是对表达式进行计数。仅当您想计算某个事件的非空出现次数时才使用Count(表达式)。


1
2
3
SET STATISTICS TIME ON

SELECT COUNT(1) FROM MyTable (nolock) -- table containing 1 million records.

SQL Server Execution Times:
CPU time = 31 ms, elapsed time = 36 ms.

1
SELECT COUNT(*) FROM MyTable (nolock) -- table containing 1 million records.

SQL Server Execution Times:
CPU time = 46 ms, elapsed time = 37 ms.

我已经运行了数百次了,每次都清除缓存。结果随服务器负载的变化而变化,但几乎总是count(*)具有更高的CPU时间。


我在SQL Server团队工作,希望能澄清这个线程中的一些要点(我以前没有见过,所以很抱歉,工程团队以前没有这样做)。

首先,select count(1) from tableselect count(*) from table之间没有语义差异。它们在所有情况下都返回相同的结果(如果不是,这是一个错误)。如其他答案所述,select count(column) from table在语义上是不同的,并不总是返回与count(*)相同的结果。

其次,在性能方面,在SQL Server(和SQL Azure)中有两个方面很重要:编译时工作和执行时工作。编译时工作是当前实现中非常少量的额外工作。由于某些内部操作在绑定和优化中的工作方式,在某些情况下,会将*扩展到所有列,然后将输出的列缩减为1列。我怀疑它是否会出现在任何可测量的测试中,而且它很可能会在覆盖下发生的所有其他事情(如自动统计、XEvent会话、查询存储开销、触发器等)的噪声中丢失。这可能是几千个额外的CPU指令。因此,在编译期间,count(1)只做了一点点的工作(这通常会发生一次,并且计划在多个后续执行之间缓存)。对于执行时间,假设计划是相同的,则不应存在可测量的差异。(前面的一个例子显示了不同之处——如果计划相同,很可能是由于机器上的其他因素造成的)。

至于计划可能会有什么不同。这是极不可能发生的,但在当前优化器的体系结构中是可能的。SQL Server的优化器作为一个搜索程序工作(想想:计算机程序对查询的不同部分进行各种备选方案的国际象棋搜索,并在合理的时间内找出最便宜的备选方案)。这个搜索对于如何在合理的时间内完成查询编译有一些限制。对于最简单的查询,搜索有几个阶段,它们根据优化器认为查询可能执行的成本处理成批的查询。有三个主要的搜索阶段,每个阶段可以运行更积极(昂贵)的启发式方法,试图找到比任何以前的解决方案更便宜的计划。最后,在每个阶段的末尾都有一个决策过程,它试图确定它是应该返回到目前为止找到的计划,还是应该继续搜索。这个过程使用到目前为止花费的总时间与目前为止找到的最佳计划的估计成本。因此,在CPU速度不同的不同机器上,可能(尽管很少)得到不同的计划,这是因为在早期阶段与继续进入下一个搜索阶段相比,计划超时。还有一些类似的场景与最后一个阶段的超时和可能在非常昂贵的查询上耗尽内存有关,这些查询占用了机器上的所有内存(在64位上通常不是问题,但在32位服务器上这是一个更大的问题)。最终,如果您得到一个不同的计划,那么运行时的性能会有所不同。我认为编译时间上的差异不太可能导致任何这些情况的发生。

网:请用你想要的两个中的任何一个,因为这些都不是任何实际的形式。(老实说,在这个主题之外,影响SQL性能的因素远不止这些)。

我希望这有帮助。我确实写了一章关于优化器是如何工作的书,但我不知道是否适合在这里发布它(因为我仍然相信它会得到很小的版税)。所以,我不会发布一个链接到我在英国的sqlbits上发表的关于优化器如何在高级别工作的演讲,这样如果你想了解的话,你可以更详细地看到搜索的不同主要阶段。以下是视频链接:https://sqlbits.com/sessions/event6/inside_the_-sql_-server_-query_-optimizer


有一篇文章表明,Oracle上的COUNT(1)只是COUNT(*)的别名,并提供了相关证据。

我将引用一些部分:

There is a part of the database software that is called"The
Optimizer", which is defined in the official documentation as
"Built-in database software that determines the most efficient way to
execute a SQL statement".

One of the components of the optimizer is called"the transformer",
whose role is to determine whether it is advantageous to rewrite the
original SQL statement into a semantically equivalent SQL statement
that could be more efficient.

Would you like to see what the optimizer does when you write a query
using COUNT(1)?

使用具有ALTER SESSION特权的用户,可以放置tracefile_identifier,启用优化器跟踪并运行COUNT(1)选择,如:SELECT /* test-1 */ COUNT(1) FROM employees;

之后,您需要对跟踪文件进行本地化,这可以使用SELECT VALUE FROM V$DIAG_INFO WHERE NAME = 'Diag Trace';完成。稍后,您将在文件中找到:

1
SELECT COUNT(*)"COUNT(1)" FROM"COURSE"."EMPLOYEES""EMPLOYEES"

如您所见,它只是COUNT(*)的别名。

另一个重要评论是:二十年前,在Oracle 7.3之前,COUNT(*)在Oracle上的速度确实更快:

Count(1) has been rewritten in count(*) since 7.3 because Oracle like
to Auto-tune mythic statements. In earlier Oracle7, oracle had to
evaluate (1) for each row, as a function, before DETERMINISTIC and
NON-DETERMINISTIC exist.

So two decades ago, count(*) was faster

对于作为SQL Server的另一个数据库,应该针对每个数据库分别进行研究。

我知道这个问题是针对SQL Server的,但是关于同一主题的其他问题(不包括数据库)已经关闭,并从这个答案中标记为重复。


计数(1)与计数(*)没有实质性的区别,如果有的话。对于计算可以为空的列的问题,可以直接演示count(*)和count()之间的区别。--

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
USE tempdb;
GO

IF OBJECT_ID( N'dbo.Blitzen', N'U') IS NOT NULL DROP TABLE dbo.Blitzen;
GO

CREATE TABLE dbo.Blitzen (ID INT NULL, Somelala CHAR(1) NULL);

INSERT dbo.Blitzen SELECT 1, 'A';
INSERT dbo.Blitzen SELECT NULL, NULL;
INSERT dbo.Blitzen SELECT NULL, 'A';
INSERT dbo.Blitzen SELECT 1, NULL;

SELECT COUNT(*), COUNT(1), COUNT(ID), COUNT(Somelala) FROM dbo.Blitzen;
GO

DROP TABLE dbo.Blitzen;
GO