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


737

只是想知道您是否有人用光Count(1)Count(*),性能是否存在明显差异,或者这仅仅是过去的日子所养成的传统习惯?

具体的数据库是SQL Server 2005


7
不了解SQL Server,但在MySQL中没有区别。另一方面,COUNT(列)不同
Greg

118
不对。COUNT(SomeColumn)将仅返回包含SomeColumn的非空值的行数。COUNT(*)和COUNT('Foo')将返回表中的总行数。
史蒂夫·布罗伯格

1
有关更多详细信息,请检查此选择计数1与选择计数*图表详细信息
Ali Adravi'5

4
哇,史蒂夫,在这里,我进入TSQL已有5年,却不知道count(*)vs Count(ColumnName)。谢谢
Harindaka

Answers:


598

没有区别。

原因:

在线书籍上说“ COUNT ( { [ [ ALL | DISTINCT ] expression ] | * } )

“ 1”是非空表达式:因此与相同COUNT(*)。优化器可以识别它是什么:琐碎的。

EXISTS (SELECT * ...或相同EXISTS (SELECT 1 ...

例:

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年8月

关于DBA.SE的类似问题

编辑,2011年12月

COUNT(*)ANSI-92中专门提到(请查找“ Scalar expressions 125”)

案件:

a)如果指定了COUNT(*),则结果为T的基数。

就是说,ANSI标准将其识别为明显的含义。由于这种迷信COUNT(1),RDBMS供应商对其进行了优化。否则将按照ANSI进行评估

b)否则,将TX设为单列表,该表是将<value expression>应用于T的每一行并消除空值的结果。如果消除了一个或多个空值,则会引发完成条件:warning-


73

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

与流行观点相反,在Oracle中,它们也这样做。

SYS_GUID() Oracle中的计算功能相当密集。

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

该查询:

SELECT  COUNT(SYS_GUID())
FROM    t_even

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

但是,此查询:

SELECT  COUNT(*)
FROM    (
        SELECT  SYS_GUID()
        FROM    t_even
        )

运行了2几秒钟,因为它甚至都没有尝试求值SYS_GUID()(尽管*是的参数COUNT(*)


它应该SYS_GUID()至少(一次)准确地评估一次子查询以返回结果,对吗?
asgs

@asgs:你为什么这么认为?如何COUNT(*)取决于的值SYS_GUID
Quassnoi

现在您问,我不确定。我以为COUNT(*)要运行,它需要一个表,因此子查询应该像一个查询。否则,我看不到COUNT(*)返回有意义的值的方法
-asgs

1
@asgs:假设您知道该map方法的作用,那么您是否看到这两个表达式:t_even.map(() => sys_guid()).length并且t_even.length总是返回相同的值?Oracle的优化器足够聪明,可以看到它并优化map零件。
Quassnoi

1
完全是@asgs。只是一个小的更正:length不太依赖什么样的收集包括,只是它的元素的数量。如果此数字存储在集合的元数据中(Oracle或大多数其他现代RDBMS并非如此,而旧MySQL的存储引擎MyISAM就是这种情况),则COUNT(*)只需从元数据中获取值即可。
Quassnoi

65

显然,COUNT(*)并且COUNT(1)始终返回相同的结果。因此,如果一个慢于另一个,那实际上是由于优化程序错误所致。由于两种形式的查询都非常频繁地使用,因此DBMS不允许此类错误保持未解决状态是没有意义的。因此,您会发现两种形式的性能在所有主要的SQL DBMS中都是(可能)相同的。


如果count(1)比count(*)慢,我不会认为这是一个错误。如果您要求dbms生成1并对不为null的那些计数,那么是的,它将归结为记录计数,但是您不能期望dbms能够检测到您编写的所有废话并为您规避。
Thorsten Kettner 2014年

1
好吧,优化器是要优化的,对于count,只有两种情况需要考虑:表达式可能为空,表达式永远不会为空:count(1)属于后者,因此DBMS不需要“生成” 1s回答问题。(顺便说一句,出于美学原因,我只用count(*)就是什么。)
Tony Andrews 2014年

46

我在SQL Server团队中工作,我希望可以澄清该线程中的几点(我以前没有看到过,所以对不起工程团队以前没有这样做)。

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

其次,关于性能,在SQL Server(和SQL Azure)中有两个方面很重要:编译时工作和执行时工作。在当前实现中,编译时间工作是很少的额外工作。在某些情况下,*会扩展到所有列,然后由于某些内部操作在绑定和优化中的工作方式,导致输出减少到1列。我怀疑它是否会出现在任何可衡量的测试中,并且很可能会被掩盖下发生的所有其他事情(例如自动状态,xevent会话,查询存储开销,触发器等)所干扰。这可能是数千条额外的CPU指令。所以,count(1)在编译期间的工作量要少一些(通常只发生一次,并且计划在多个后续执行中进行缓存)。对于执行时间,假设计划相同,则不应有可测量的差异。(较早的示例之一显示出差异-如果计划相同,则很可能是由于计算机上的其他因素引起的)。

关于计划可能如何有所不同。这些是极不可能发生的,但在当前优化器的体系结构中有可能实现。SQL Server的优化器用作搜索程序(请考虑:计算机程序下棋,通过各种替代方法搜索查询的不同部分,并花费替代方法以在合理的时间内找到最便宜的计划)。该搜索在如何保持查询编译在合理的时间内完成方面有一些限制。对于最琐碎的查询,存在搜索的各个阶段,它们根据优化器认为查询可能执行的成本来处理查询。有3个主要搜索阶段,每个阶段都可以运行更具进取性(昂贵)的启发式尝试,以寻找比任何现有解决方案都便宜的计划。最终,每个阶段的末尾都有一个决策过程,试图确定是应该返回到目前为止找到的计划还是应该继续搜索。此过程使用了到目前为止所花费的总时间与迄今为止找到的最佳计划的估计成本。因此,在具有不同CPU速度的不同机器上,有可能(尽管很少)获得不同的计划,这是由于在较早的阶段中有计划而与在下一个搜索阶段中继续相比会超时。还有一些类似的场景与超时有关,可能会在非常非常昂贵的查询上耗尽内存,这些查询消耗了计算机上的所有内存(通常不会在64位上出现问题,但这是一个更大的问题返回32位服务器)。最终,如果您获得了不同的计划,则运行时的性能将有所不同。我不

网络:请使用您想要的两者中的任何一个,因为任何实际形式都无关紧要。(老实说,有很多影响SQL性能的因素远远超出了本主题)。

我希望这有帮助。我确实写了一本书关于优化器如何工作的章节,但是我不知道是否适合将其发布在这里(因为我仍然从中获得很小的版税)。因此,除了发布文章之外,我不会发布指向我在英国SQLBits上发表的有关优化器如何在较高水平下工作的演讲的链接,因此您可以根据需要更详细地了解搜索的不同主要阶段了解这一点。这是视频链接:https : //sqlbits.com/Sessions/Event6/inside_the_sql_server_query_optimizer


2
我的信念是,它1也会经历同样的扩展。我基于此处的性能测试stackoverflow.com/questions/1597442/… 还看到了示例示例,其中查询的答案使用了1列级权限时发生意外失败
Martin Smith,

21

在SQL-92标准中, COUNT(*)专门表示“表表达式的基数”(可以是基表,VIEW,派生表,CTE等)。

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

同样,COUNT(*)熟悉SQL标准的人类程序员也可以轻松地挑选出来,这是与多个供应商提供的SQL产品一起使用时的一项有用技能。

同样,在特殊情况下SELECT COUNT(*) FROM MyPersistedTable;,思想是DBMS可能会保存表基数的统计信息。

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


1
SQL-92文本从我的答案链接上DBA.SE:dba.stackexchange.com/questions/2511/...
GBN


12

我希望优化器确保在奇怪的边缘情况下没有真正的区别。

像其他任何事情一样,唯一真实的说法就是衡量您的具体案例。

就是说,我一直都习惯COUNT(*)


根据公认的答案,这对于MS SQL而言并非如此-两者之间实际上没有区别。
David Manheim 2012年

10

随着这个问题一遍又一遍地出现,这里是另一个答案。我希望为初学者增加一些关于“最佳实践”的信息。

SELECT COUNT(*) FROM something 计算记录很容易。

SELECT COUNT(1) FROM something 每条记录检索1,然后计算不为null的1(实际上是对记录进行计数),只是更为复杂。

话虽如此:好的dbms注意,第二条语句将产生与第一条语句相同的计数,并相应地重新解释它,以免进行不必要的工作。因此,通常这两个语句将导致相同的执行计划并花费相同的时间。

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


8

我在SQL Server 2012的8 GB RAM hyper-v盒上进行了快速测试。您可以自己查看结果。在运行这些测试时,除了SQL Server Management Studio之外,我没有运行任何其他窗口应用程序。

我的表架构:

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亿行)

第一个查询:

Set Statistics Time On
Go    
Select Count(*) From Employee
Go    
Set Statistics Time Off
Go

首次查询结果:

 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.

第二个查询:

    Set Statistics Time On
    Go    
    Select Count(1) From Employee
    Go    
    Set Statistics Time Off
    Go

第二次查询的结果:

 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 Engine表现出的任何性能差异。

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


7
SET STATISTICS TIME ON

select count(1) from MyTable (nolock) -- table containing 1 million records. 

SQL Server执行时间:
CPU时间= 31毫秒,经过的时间= 36毫秒。

select count(*) from MyTable (nolock) -- table containing 1 million records. 

SQL Server执行时间:
CPU时间= 46毫秒,经过的时间= 37毫秒。

我已经运行了数百次,每次都清除高速缓存。随着服务器负载的变化,结果有时会不尽相同,但几乎总是count(*)会有更高的cpu时间。


14
我无法重现。count(*)并且count(1)在我的SQL 2008实例中,即使在计数包含450万行的表时,也可以在几毫秒内返回结果。
杰夫·阿特伍德

2
有时,在某些系统中,首先运行的语句始终运行得更快...您是否随机化了它们的运行顺序?
JosephDoggie

@JosephDoggie在进行此类测量/统计时,应始终在运行每个查询之前重新启动SQL Server服务。当您刚刚启动SQL Server服务时,每次运行都变得完全独立,并且查询顺序无关紧要。另一方面,如果不重新启动SQL Server服务,并且引擎执行某种形式的执行计划缓存,则稍后运行的查询应该比第一个运行得更快。
RBT

执行时间需要在进行比较时查看确切的查询计划。如果它们不同(例如,哈希聚合与排序+流聚合),则结果不可比。因此,我敦促谨慎地在没有更多数据的情况下得出结论。
科纳·坎宁安MSFT,

3

一篇文章表明COUNT(1)on Oracle只是的别名COUNT(*),并对此进行了证明

我会引用一些部分:

数据库软件的一部分称为“优化程序”,在官方文档中定义为“确定执行SQL语句最有效方式的内置数据库软件”。

优化程序的组件之一称为“转换器”,其作用是确定将原始SQL语句重写为语义上等效的SQL语句(可能更有效)是否有利。

您想查看使用COUNT(1)编写查询时优化器的作用吗?

对于具有ALTER SESSION特权的用户,您可以放置tracefile_identifier,启用优化程序跟踪并运行COUNT(1)select,例如:SELECT /* test-1 */ COUNT(1) FROM employees;

之后,您需要本地化跟踪文件,可以使用做什么SELECT VALUE FROM V$DIAG_INFO WHERE NAME = 'Diag Trace';。在文件的后面,您将找到:

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

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

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

自7.3起,Count(1)已被重写为count(*),因为Oracle喜欢自动调整神话语句。在较早的Oracle7中,oracle必须在存在DETERMINISTIC和NON-DETERMINISTIC之前,对每行的函数求值(1)。

所以二十年前,count(*)更快

对于另一个数据库(如Sql Server),应针对每个数据库分别进行研究。

我知道这个问题是特定于Sql Server的,但是关于同一主题的其他问题(不提及数据库)已被关闭,并标记为该答案的重复项。



0

如果有的话,COUNT(1)与COUNT(*)并没有太大的不同。关于计数可为空的列的问题,这可以很容易地演示COUNT(*)和COUNT(<some col>)之间的区别-

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.