计算总和(列)


9

我有这段代码,用于总结某个商品(itemid)的数量及其产品日期代码(proddte)。

select sum(qty), itemid, proddte 
from testtable where .... 
group by itemid, proddte

我想做的就是qty不计其数地求和itemid/proddte。我努力了:

select sum(qty), itemid, proddte, sum(qty) over() as grandtotal 
from testtable 
where .... 
group by itemid, proddte

但是它说我也应该qtygroup by条款中。如果这样做,结果将不会与预期结果相同。

它不一定需要表示为单独的列,每一行中的值都相同。只要我可以显示整体总数,任何表示形式都可以接受。

Answers:


9
CREATE TABLE #foo
(
 itemid int, 
 proddte date,
 qty int
);

INSERT #foo(itemid,proddte,qty) VALUES
(1,'20140101',5),(1,'20140102',7),(2,'20150101',10);


-- if it really needs to be a column with the same value
-- in every row, just calculate once and assign it to a variable

DECLARE @sum int = (SELECT SUM(qty) FROM #foo);

SELECT itemid, proddte, GroupedSum = SUM(qty), GrandTotal = @sum
  FROM #foo
  GROUP BY itemid, proddte;

-- if the grand total can be expressed on its own row, 
-- you can use GROUP BY GROUPING SETS:
SELECT itemid, proddte, SUM(qty)
  FROM #foo GROUP BY GROUPING SETS((),(itemid,proddte));

-- if that syntax is confusing, you can use a less
-- efficient UNION ALL:
SELECT itemid, proddte, SUM(qty)
  FROM #foo GROUP BY itemid,proddte
UNION ALL
SELECT NULL, NULL, SUM(qty) 
  FROM #foo;

GO
DROP TABLE #foo;

GROUP BY GROUPING SETSIS基本上是一个UNION ALL。这()意味着SUM无论分组如何,列出的任何其他组都将单独汇总。尝试GROUP BY GROUPING SETS ((itemid),(itemid,proddte))看看区别。

有关更多详细信息,请参见文档:

结合使用GROUP BY和ROLLUP,CUBE和GROUPING SETS

如Andriy所述,上面的查询也可以使用以下代码编写:

GROUP BY ROLLUP( (itemid,proddte) )

请注意,两列用另一对括号括起来,使它们成为一个单位。Andriy 编写了一个托管在Stack Exchange Data Explorer上的演示


1
@niq:GROUP BY ROLLUP((itemid,proddte))会产生相同的结果,并且可能会减少混乱。
Andriy M

@AndriyM并不等效,因为它将包括itemidIe的一个总计。等效于GROUP BY GROUPING SETS((),(itemid),(itemid,proddte))
Martin Smith

4
@MartinSmith:不,这些列用另外一对方括号括起来,这使它们成为一个单元。GROUP BY ROLLUP(itemid,proddte)另一方面,确实会在itemid(与GROUP BY ROLLUP((itemid),(proddte)))上产生(附加)小计。SEDE上的演示
Andriy M

3
@AndriyM我已纠正。尽管这样做确实破坏了“较少混淆”的观点,因为它设法使至少一个人混淆:-)
Martin Smith

2
@AndriyM我发现并没有GROUP BY ROLLUP那么混乱,但这是很主观的。当我阅读类似的东西时,我也会总是感到紧张The non-ISO compliant WITH ROLLUP, WITH CUBE, and ALL syntax is deprecated-为什么我倾向于青睐GROUPING SETS
亚伦·伯特兰

10

这也是有效的语法:

       sum(sum(qty)) over ()

初次看到它时会有些混乱,但是您只需要记住,例如sum() over (),在应用了窗口函数之后group by,就可以将所有可以通过查询显示在组选择列表中的内容都放在窗口集合中。所以(qty不能不可以)sum(qty)可以放在里面sum() over ()

select sum(qty), itemid, proddte, 
       sum(sum(qty)) over () as grandtotal  
from testtable 
where .... 
group by itemid, proddte ;

话虽如此,我更喜欢GROUPING SETSAaron Bertrand提供的查询。总金额需要显示一次,而不是每行显示一次。

还要注意,虽然总和可以用于计算总和,但是如果您想要总和,则必须使用总和(而不是总和!):

sum(count(*)) over ()  as grand_count

如果要在所有表上求平均值,那就更加复杂了:

sum(sum(qty)) over ()
/ sum(count(qty)) over ()  as grand_average

因为平均值的平均值与所有平均值的平均值不同。(如果尝试使用,avg(avg(qty)) over ()您会发现它产生的结果可能不同于上述总体平均值。)


3

一种可能的解决方法是将第一个包装GROUP BYCTE中

WITH
CTE
AS
(
    select
        itemid
        ,proddte
        ,sum(qty) AS SumQty
    from testtable 
    where .... 
    group by itemid, proddte
)
SELECT
    itemid
    ,proddte
    ,SumQty
    ,SUM(SumQty) OVER () AS grandtotal
FROM CTE
;

3
ypercube的答案说明,不需要CTE
Martin Smith

1
@MartinSmith,你是对的。任何非递归CTE都可以重写为某种形式的子查询。SQL Server的优化器无论如何都内联CTE(例如,与Postgres相对),因此无论有无CTE,执行计划都是相同的。但是,如果使用CTE将复杂的查询分解为更简单的部分,则通常更易于阅读和理解。至少对我来说。
弗拉基米尔·巴拉诺夫(Fladimir Baranov)

3
我认为您误解了Martin的观点,尽管您对CTE增加可读性的观点可能仍然成立。ypercube的建议表明,在这种情况下,您可以避免使用任何形式的子查询,无论是CTE,派生表还是计算列作为标量聚合子查询。
Andriy M

1
@AndriyM,我喜欢ypercube的答案的变体,在这里看到它之前我没有想到这样的语法。学习新知识总是好的。没错,我的主要观点归结为可读性。在我的测试中,无论是否使用CTE,优化程序都会生成相同的执行计划。
弗拉基米尔·巴拉诺夫(Fladimir Baranov)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.