跨列复制时,SQL Server 缓存是否聚合结果?

Dav*_*rer 6 sql-server sql-server-2012

假设我们有一个Orders包含列的表order_id, total, discount

然后我们写一个类似于下面的查询

SELECT
    COUNT(order_id) AS num_orders
    , SUM(total) / COUNT(order_id) as avg_total
    , SUM(discount) / COUNT(order_id) AS avg_discount
FROM Orders
Run Code Online (Sandbox Code Playgroud)

COUNT(order_id)Preserved的值是跨列还是重新计算(例如,是否有性能影响)?或者最好先确定计算值并在查询中使用这些值,例如:

DECLARE @order_count AS INT 

SELECT 
    @order_count = COUNT(order_id) 
FROM Orders

SELECT
    @order_count AS num_orders
    , SUM(total) / @order_count as avg_total
    , SUM(discount) / @order_count AS avg_discount
FROM Orders
Run Code Online (Sandbox Code Playgroud)

请注意,在编写此问题时,我注意到由于AVG()支持SQL Server 2008 。但是,我继续了这个问题,并打算更一般地想了解 SQL Server 如何处理跨列的相同聚合,因为我有时会以其他形式遇到这个问题。

Mar*_*ith 10

SQL Server 只计算COUNT一次。您可以通过查看执行计划的属性来看到这一点

create table Orders(order_id int, total int, discount int)


SELECT
    COUNT(order_id) AS num_orders
    , SUM(total) / COUNT(order_id) as avg_total
    , SUM(discount) / COUNT(order_id) AS avg_discount
FROM Orders
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

流聚合 (1) 具有以下定义的值

[Expr1008] = Scalar Operator(COUNT([tempdb].[dbo].[Orders].[order_id])), 
[Expr1009] = Scalar Operator(COUNT_BIG([tempdb].[dbo].[Orders].[total])), 
[Expr1010] = Scalar Operator(SUM([tempdb].[dbo].[Orders].[total])), 
[Expr1011] = Scalar Operator(COUNT_BIG([tempdb].[dbo].[Orders].[discount])), 
[Expr1012] = Scalar Operator(SUM([tempdb].[dbo].[Orders].[discount]))
Run Code Online (Sandbox Code Playgroud)

Expr1008COUNT您询问的计算。

COUNT其他两列还有一些其他聚合。这些是必需的,因为SUM(total)(例如) if COUNT(total)is的正确结果0应该是NULL

这由沿 (2) 的下一个计算标量执行。这也将COUNT结果 ( Expr1008) 从转换bigintint并标记为Expr1003

[Expr1003] = Scalar Operator(CONVERT_IMPLICIT(int,[Expr1008],0)),
[Expr1004] = Scalar Operator(CASE WHEN [Expr1009]=(0) THEN NULL ELSE [Expr1010] END), 
[Expr1005] = Scalar Operator(CASE WHEN [Expr1011]=(0) THEN NULL ELSE [Expr1012] END)
Run Code Online (Sandbox Code Playgroud)

最后最左边的计算标量 (3) 用于Expr1003除法运算......

[Expr1006] = Scalar Operator([Expr1004]/[Expr1003]), 
[Expr1007] = Scalar Operator([Expr1005]/[Expr1003])
Run Code Online (Sandbox Code Playgroud)

...并输出列Expr1003, Expr1006, Expr1007作为最终结果

PS:AVG支持的时间比 SQL Server 2008 长得多。我想它可能一开始就可用。但是,NULL无论如何,它与您在s存在的情况下重写的语义不同。

我假设order_id是主键,因此不可为空,但对于具有 10 个订单和两个NOT NULL total值的表24然后AVG(total)将是3SUM(total) / COUNT(order_id)将是0.6(或0一旦考虑整数除法)。