Compute the total of SUM(column)
This is valid syntax, too:
sum(sum(qty)) over ()
It's a bit confusing when one sees it at first but you only have to remember that the window functions - e.g. sum() over ()
- are applied after the group by
so everything that can appear in the select list of a group by query can be placed inside a window aggregate. So (the qty
cannot but) the sum(qty)
can be placed inside sum() over ()
:
select sum(qty), itemid, proddte,
sum(sum(qty)) over () as grandtotal
from testtable
where ....
group by itemid, proddte ;
Having said that, I'd prefer the GROUPING SETS
query provided by Aaron Bertrand. The total sum needs to be shown once and not in every row.
Also note that while the sum of sums can be used to calculate the total sum, if you wanted the total count, you'd have to use the sum of counts (and not the count of counts!):
sum(count(*)) over () as grand_count
And if one wanted the average over all the table, it would be even more complicated:
sum(sum(qty)) over ()
/ sum(count(qty)) over () as grand_average
because the average of averages is not the same as the average over all. (If you try the avg(avg(qty)) over ()
you'll see that it may yield a different result than the above grand average.)
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;
The GROUP BY GROUPING SETS
is is basically a UNION ALL
. The ()
means just take the SUM
regardless of grouping, any other group listed gets aggregated separately. Try GROUP BY GROUPING SETS ((itemid),(itemid,proddte))
to see the difference.
For more details see the documentation:
Using GROUP BY with ROLLUP, CUBE, and GROUPING SETS
As Andriy mentioned, the query above could also be written using:
GROUP BY ROLLUP( (itemid,proddte) )
Note the two columns there are enclosed in an additional pair of parentheses, making them a single unit. Andriy wrote a demo hosted on the Stack Exchange Data Explorer.
One possible way around is to wrap the first GROUP BY
into CTE:
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
;