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

时间:2009-08-03 10:15:11

标签: sql sql-server performance

只是想知道你们中是否有人使用Count(1)超过Count(*)并且是否存在明显的性能差异,或者这只是过去几天提出的遗留习惯? / p>

(特定数据库为SQL Server 2005。)

14 个答案:

答案 0 :(得分:561)

没有区别。

原因:

  

Books on-line说“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月

Similar question on DBA.SE

编辑,2011年12月

ANSI-92中专门提到了

COUNT(*)(查找“Scalar expressions 125”)

  

案例:

     

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

也就是说,ANSI标准认为它明显是你的意思。 RDBMS供应商已经优化了COUNT(1),因为这种迷信。否则,将根据ANSI

进行评估
  

b)否则,让TX成为单列表                 应用< value expression>的结果到T的每一行                 并消除空值。如果有一个或多个空值                 消除,然后提出完成条件:警告 -

答案 1 :(得分:70)

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

与流行的观点相反,在甲骨文中也是如此。

Oracle中的

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

在我的测试数据库中,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(*)的参数)

答案 2 :(得分:58)

显然,COUNT(*)和COUNT(1)将始终返回相同的结果。因此,如果一个比另一个慢,那实际上是由于一个优化器错误。由于这两种形式在查询中经常使用,因此DBMS允许这样的错误保持不固定是没有意义的。因此,您会发现两种形式的性能在所有主要的SQL DBMS中都是相同的。

答案 3 :(得分:20)

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

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

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

此外,在特殊情况SELECT COUNT(*) FROM MyPersistedTable;中,我们的想法是DBMS可能会保存表的基数的统计信息。

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

答案 4 :(得分:16)

COUNT(*)COUNT(1)在结果和效果方面相同。

答案 5 :(得分:12)

我希望优化者能够确保在奇怪的边缘情况之外没有真正的区别。

与任何事情一样,唯一真实的方法就是衡量你的具体案例。

那就是说,我总是使用COUNT(*)

答案 6 :(得分:8)

随着这个问题一再出现,这里还有一个答案。我希望为初学者添加一些东西,让他们了解最佳实践"这里。

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

SELECT COUNT(1) FROM something检索每条记录1,而不是计算非空的1,这实际上是计算记录,只是更复杂。

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

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

答案 7 :(得分:8)

我在8 GB RAM hyper-v盒子上对SQL Server 2012进行了快速测试。您可以自己查看结果。在运行这些测试时,我没有运行除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引擎所展示的任何性能差异。

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

答案 8 :(得分:7)

SET STATISTICS TIME ON

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

SQL Server执行时间:
  CPU时间= 31 ms,经过时间= 36 ms。

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

SQL Server执行时间:
  CPU时间= 46 ms,经过时间= 37 ms。

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

答案 9 :(得分:5)

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

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

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

答案 10 :(得分:3)

有一个article显示 Oracle 上的COUNT(1)只是COUNT(*)的别名,其中证明这一点。

我会引用一些部分:

  

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

     

优化器的一个组件称为“变压器”,   其作用是确定重写是否有利   将原始SQL语句转换为语义上等效的SQL语句   这可能更有效率。

     

您想要查看编写查询时优化程序的功能吗?   使用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';完成的操作。稍后在文件中,您将找到:

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

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

另一个重要评论:在Oracle 7.3之前,COUNT(*)在Oracle上真的更快two decades ago

  

Count(1)已经在count(*)中重写了7.3,因为Oracle喜欢   自动调整神话语句。在早期的Oracle7中,oracle必须这样做   在DETERMINISTIC和之前,为每一行评估(1)作为函数   存在非决定因素。

     

二十年前,计数(*)更快

对于另一个数据库作为Sql Server,应该为每个数据库进行单独研究。

我知道这个问题是针对Sql Server的,但关于同一主题的其他问题,未提及数据库,已被关闭,并在此答案中标记为重复。

答案 11 :(得分:1)

在所有RDBMS中,两种计数方式在产生的结果方面都是等效的。关于性能,我没有观察到SQL Server的任何性能差异,但是值得指出的是,一些RDBMS e.g. PostgreSQL 11, have less optimal implementations for COUNT(1) as they check for the argument expression's nullability as can be seen in this post

我发现运行100万行的性能有10%的差异:

-- Faster
SELECT COUNT(*) FROM t;

-- 10% slower
SELECT COUNT(1) FROM t;

答案 12 :(得分:-1)

COUNT(1)与COUNT(*)基本上没有差别,如果有的话。至于COUNTing NULLable COLUMNs的问题,这可以直接演示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

答案 13 :(得分:-1)

如果您在 SQL Server 中运行以下代码,您会注意到 COUNT(1) 无论如何都被评估为 COUNT(*)。所以看起来没有区别,而且 COUNT(*) 是查询优化器最原生的表达式:

SET SHOWPLAN_TEXT ON
GO

SELECT COUNT(1)
FROM <table>
GO

SET SHOWPLAN_TEXT OFF
GO