提问



只是想知道你们中是否有人使用Count(1)而不是Count(*),如果性能有显着差异,或者这只是从过去几天提出的遗留习惯?


(具体数据库是SQL Server 2005。)

最佳参考


没有区别。


原因:



  在线图书说COUNT ( { [ [ ALL | DISTINCT ] expression ] | * } )[59]



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的类似问题[60]


编辑,2011年12月


COUNT(*)在ANSI-92中具体提到(寻找Scalar expressions 125)[61]



  案件:

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



也就是说,ANSI标准认为它明显是你的意思。 COUNT(1)已被RDBMS供应商优化,因为这种迷信。否则,它将按照ANSI进行评估



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


其它参考1


在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(*)的参数)

其它参考2


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

其它参考3


在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


COUNT(*)COUNT(1)在结果和表现方面是相同的。

其它参考5


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


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


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

其它参考6


我在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服务。

其它参考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时间。

其它参考8


随着这个问题一再出现,这里还有一个答案。我希望在这里为初学者添加一些想知道最佳实践的东西。


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


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


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


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

其它参考9


有一篇文章显示 Oracle 上的COUNT(1)只是COUNT(*)的别名,其中证明[62]


我会引用一些部分:



  有一部分数据库软件被称为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(*)的别名。


另一个重要的评论:二十年前
 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.
在Oracle 7.3之前COUNT(*)真的更快了:[63]



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

  
  二十年前,伯爵(*)更快



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


我知道这个问题是特定于Sql Server的,但关于同一主题的其他问题,没有提到数据库,已被关闭并标记为从此答案重复。

其它参考10


易于演示COUNT(*)vs 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