只是想知道你们中是否有人使用Count(1)而不是Count(*),是否在性能上有明显的差异,或者这只是过去几天养成的传统习惯?
特定的数据库是SQL Server 2005。
只是想知道你们中是否有人使用Count(1)而不是Count(*),是否在性能上有明显的差异,或者这只是过去几天养成的传统习惯?
特定的数据库是SQL Server 2005。
当前回答
我在一个8GB的RAM超级存储箱上对SQL Server 2012进行了快速测试。您可以自己查看结果。在运行这些测试时,我没有运行除SQLServerManagementStudio之外的任何其他窗口应用程序。
我的表架构:
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
雇员表中的记录总数: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服务。
其他回答
SET STATISTICS TIME ON
select count(1) from MyTable (nolock) -- table containing 1 million records.
SQL Server执行时间:CPU时间=31ms,经过时间=36ms。
select count(*) from MyTable (nolock) -- table containing 1 million records.
SQL Server执行时间:CPU时间=46 ms,经过时间=37 ms。
我已经运行了数百次,每次都清除缓存。。结果随服务器负载的变化而变化,但几乎总是count(*)具有较高的cpu时间。
我在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
随着这个问题一次又一次地出现,这里还有一个答案。我希望在这里为初学者添加一些关于“最佳实践”的内容。
SELECT COUNT(*)FROM something计数记录,这是一项简单的任务。
SELECT COUNT(1)FROM从某个对象中检索每条记录的1,然后对不为空的1进行计数,这实际上是对记录进行计数,只是更复杂。
话虽如此:好的dbms注意到,第二条语句将产生与第一条语句相同的计数,并相应地重新解释它,以免做不必要的工作。因此,通常这两个语句将产生相同的执行计划,并花费相同的时间。
但是,从可读性的角度来看,您应该使用第一条语句。您要计算记录,所以要计算记录而不是表达式。仅当您希望计算某个事件的非空出现时,才使用COUNT(表达式)。
在SQL-92标准中,COUNT(*)特别表示“表表达式的基数”(可以是基表、`VIEW、派生表、CTE等)。
我认为COUNT(*)很容易解析。使用任何其他表达式都需要解析器确保它不引用任何列(COUNT('a'),其中a是文本,COUNT(a),如果a是列,可以产生不同的结果)。
同样,COUNT(*)可以由熟悉SQL标准的程序员轻松识别,这是一种在使用多个供应商的SQL产品时非常有用的技能。
此外,在特殊情况下,SELECT COUNT(*)FROM MyPersistedTable;,其思想是DBMS可能保存表基数的统计信息。
因此,因为COUNT(1)和COUNT(*)在语义上是等价的,所以我使用COUNT(*)。
在SQL Server中,这些语句产生相同的计划。
与流行的观点相反,在甲骨文公司,他们也是如此。
Oracle中的SYS_GUID()是一个计算密集型函数。
在我的测试数据库中,t_even是一个包含1000000行的表
此查询:
SELECT COUNT(SYS_GUID())
FROM t_even
运行48秒,因为函数需要计算返回的每个SYS_GUID(),以确保它不是NULL。
但是,此查询:
SELECT COUNT(*)
FROM (
SELECT SYS_GUID()
FROM t_even
)
运行仅2秒,因为它甚至不尝试计算SYS_GUID()(尽管*是COUNT(*)的参数)