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

特定的数据库是SQL Server 2005。


当前回答

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

我将引用一些部分:

数据库软件的一部分叫做“Optimizer”,在官方文档中定义为“内置数据库软件,可确定执行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(*)的别名。

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

自7.3以来,计数(1)已重写为计数(*),因为Oracle类似自动调整mythic语句。在早期的Oracle7中,oracle必须在确定之前对每一行求值(1),作为函数存在非确定性。20年前,count(*)更快

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

我知道这个问题是针对SQL Server的,但SO中关于同一主题的其他问题(没有提到特定的数据库)已关闭,并标记为与此答案重复。

其他回答

在所有RDBMS中,这两种计数方式在产生什么结果方面是等价的。关于性能,我没有在SQL Server中观察到任何性能差异,但值得指出的是,一些RDBMS,例如PostgreSQL 11,在检查参数表达式的可空性时,COUNT(1)的实现不太理想,如本文所示。

我发现运行以下命令时,1M行的性能差异为10%:

-- Faster
SELECT COUNT(*) FROM t;

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

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

我将引用一些部分:

数据库软件的一部分叫做“Optimizer”,在官方文档中定义为“内置数据库软件,可确定执行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(*)的别名。

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

自7.3以来,计数(1)已重写为计数(*),因为Oracle类似自动调整mythic语句。在早期的Oracle7中,oracle必须在确定之前对每一行求值(1),作为函数存在非确定性。20年前,count(*)更快

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

我知道这个问题是针对SQL Server的,但SO中关于同一主题的其他问题(没有提到特定的数据库)已关闭,并标记为与此答案重复。

没有区别。

原因:

在线书籍说“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月

ANSI-92中特别提到了COUNT(*)(查找“标量表达式125”)

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

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

b) 否则,让TX是单列表,即将<value表达式>应用于T的每一行的结果以及消除空值。如果一个或多个空值消除,则引发完成条件:警告-

我在SQL Server团队工作,我希望能澄清这篇文章中的几点(我以前没有看过,所以很抱歉工程团队以前没有这样做)。

首先,从表中选择计数(1)与从表中的选择计数(*)之间没有语义差异。它们在所有情况下都返回相同的结果(如果不是,则是错误)。正如其他答案中所指出的,从表中选择count(column)在语义上是不同的,并不总是返回与count(*)相同的结果。

其次,关于性能,SQL Server(和SQL Azure)中有两个方面很重要:编译时工作和执行时工作。在当前的实现中,编译时间工作是一项微不足道的额外工作。在某些情况下,*扩展到所有列,然后由于某些内部操作在绑定和优化中的工作方式,输出的列减少到1列。我怀疑它是否会出现在任何可测量的测试中,而且它很可能会迷失在幕后发生的所有其他事情(如自动统计、xevent会话、查询存储开销、触发器等)的噪音中。这可能是数千条额外的CPU指令。因此,count(1)在编译过程中所做的工作很少(通常只会发生一次,计划会在多个后续执行中缓存)。对于执行时间,假设计划相同,应该没有可测量的差异。(前面的一个示例显示了一个差异-如果计划相同,则很可能是由于机器上的其他因素)。

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

Net Net:请使用您想要的两个选项中的任何一个,因为这在任何实际形式中都不重要。(老实说,影响SQL性能的因素远不止这个主题)。

我希望这有帮助。我确实写了一章关于优化器如何工作的书,但我不知道是否适合将其发布在这里(因为我仍然从中获得少量版税)。因此,我将发布一个链接,链接到我在英国SQLBits所做的关于优化器如何在高级别上工作的演讲,这样,如果你想了解这一点,你可以更详细地看到搜索的不同主要阶段。以下是视频链接:https://sqlbits.com/Sessions/Event6/inside_the_sql_server_query_optimizer

COUNT(1)与COUNT。关于计数空列的问题,这可以直接演示COUNT(*)和COUNT之间的差异(<somecol>)--

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