Reputation: 391
I'm executing a query that gets a couple thousands rows as result, and the customer needs a row showing the sum totals of certain numeric columns. I've achieved this by using group by grouping sets, but this function supports up to 32 columns that are not in aggregate functions. My problem is that I have nearly 45 columns that I have to return, being only 10 that I leave out of the group by because of aggregate functions.
Original query was something like this:
select
o.Name,
ci.Id,
ci.OriginId,
ci.Varchar1,
ci.Varchar2,
ci.Varchar3,
ci.Varchar4,
ci.Varchar5,
ci.Varchar6,
ci.Varchar7,
ci.Varchar8,
ci.Varchar9,
ci.Varchar10,
ci.Varchar11,
ci.Varchar12,
ci.Varchar13,
ci.Varchar14,
ci.Varchar15,
ci.Varchar16,
ci.Varchar17,
ci.Varchar18,
ci.Varchar19,
ci.Varchar20,
sum(ci.Decimal1) as Decimal1,
sum(ci.Decimal1) as Decimal2,
sum(ci.Decimal1) as Decimal3,
sum(ci.Decimal1) as Decimal4,
sum(ci.Decimal1) as Decimal5,
sum(ci.Decimal1) as Decimal6,
sum(ci.Decimal1) as Decimal7,
sum(ci.Decimal1) as Decimal8,
sum(ci.Decimal1) as Decimal9,
sum(ci.Decimal1) as Decimal10,
ci.Date1,
ci.Date2,
ci.Date3,
ci.Date4,
ci.Date5,
ci.Date6,
ci.Date7,
ci.Date8,
ci.Date9,
ci.Date10
from
Items ci
inner join Origins o
on ci.OriginId = o.Id
group by grouping sets((
o.Name,
ci.Id,
ci.OriginId,
ci.Varchar1,
ci.Varchar2,
ci.Varchar3,
ci.Varchar4,
ci.Varchar5,
ci.Varchar6,
ci.Varchar7,
ci.Varchar8,
ci.Varchar9,
ci.Varchar10,
ci.Varchar11,
ci.Varchar12,
ci.Varchar13,
ci.Varchar14,
ci.Varchar15,
ci.Varchar16,
ci.Varchar17,
ci.Varchar18,
ci.Varchar19,
ci.Varchar20,
ci.Date1,
ci.Date2,
ci.Date3,
ci.Date4,
ci.Date5,
ci.Date6,
ci.Date7,
ci.Date8,
ci.Date9,
ci.Date10), ())
I've tried to split the query in two, so that the amount of columns in the group by doesn't reach the maximum available. If I execute each query separated I get the desired results, but if I union them I have an error (can't convert nvarchar to numeric).
The result was something like this:
select
o.name
ci.Id,
ci.OriginId,
sum(ci.Decimal1) as Decimal1,
sum(ci.Decimal1) as Decimal2,
sum(ci.Decimal1) as Decimal3,
sum(ci.Decimal1) as Decimal4,
sum(ci.Decimal1) as Decimal5,
sum(ci.Decimal1) as Decimal6,
sum(ci.Decimal1) as Decimal7,
sum(ci.Decimal1) as Decimal8,
sum(ci.Decimal1) as Decimal9,
sum(ci.Decimal1) as Decimal10,
ci.Date1,
ci.Date2,
ci.Date3,
ci.Date4,
ci.Date5,
ci.Date6,
ci.Date7,
ci.Date8,
ci.Date9,
ci.Date10
from
Items ci
inner join Origins o
on ci.OriginId = o.Id
group by grouping sets((
o.Name,
ci.Id,
ci.OriginId,
ci.Date1,
ci.Date2,
ci.Date3,
ci.Date4,
ci.Date5,
ci.Date6,
ci.Date7,
ci.Date8,
ci.Date9,
ci.Date10), ())
union
select
o.Name,
ci.Id,
ci.OriginId,
ci.Varchar1,
ci.Varchar2,
ci.Varchar3,
ci.Varchar4,
ci.Varchar5,
ci.Varchar6,
ci.Varchar7,
ci.Varchar8,
ci.Varchar9,
ci.Varchar10,
ci.Varchar11,
ci.Varchar12,
ci.Varchar13,
ci.Varchar14,
ci.Varchar15,
ci.Varchar16,
ci.Varchar17,
ci.Varchar18,
ci.Varchar19,
ci.Varchar20
from
Items ci
inner join Origins o
on ci.OriginId = o.Id
group by grouping sets((
o.name,
ci.Id,
ci.OriginId,
ci.Varchar1,
ci.Varchar2,
ci.Varchar3,
ci.Varchar4,
ci.Varchar5,
ci.Varchar6,
ci.Varchar7,
ci.Varchar8,
ci.Varchar9,
ci.Varchar10,
ci.Varchar11,
ci.Varchar12,
ci.Varchar13,
ci.Varchar14,
ci.Varchar15,
ci.Varchar16,
ci.Varchar17,
ci.Varchar18,
ci.Varchar19,
ci.Varchar20), ())
Another way (if possible), would be to drop the group by grouping sets in SQL and generate a row with C#, since the result of the query is recieved by a IEnumerable, but I don't know if a SUM function is available.
Any advice will be appreciated.
Thanks in advance.
Upvotes: 1
Views: 286
Reputation: 5120
If what you are trying to do is basically all data plus total row, consider the following approach. Do not group by grouping set that include all non-aggregated columns, instead group by row ID (existing one, which should be unique within all data rows, or artificial, created with row_number()
function). Also consider joining auxiliary tables after total is calculated.
The example follows.
Setup sample data:
declare @origs table (id int, name varchar(20));
insert into @origs values (1, 'orig1'), (2, 'orig2');
declare @items table (
id int, orig_id int,
column1 varchar(20), column2 varchar(20),
value1 float, value2 float);
insert into @items values
(1, 1, 'c1.1', 'c2.1', 100, 10)
,(2, 1, 'c1.2', 'c2.2', 200, 20)
,(3, 2, 'c1.3', 'c2.3', 300, 30);
The query below returns all data plus total row the way you are trying to do it:
select i.id, o.name as orig, i.column1, i.column2, sum(i.value1) val1, sum(i.value2) val2
from @items i
join @origs o on o.id = i.orig_id
group by grouping sets ((i.id, o.name, i.column1, i.column2), ());
The output is:
id orig column1 column2 val1 val2
----- ----- -------- -------- ----- -----
1 orig1 c1.1 c2.1 100 10
2 orig1 c1.2 c2.2 200 20
3 orig2 c1.3 c2.3 300 30
NULL NULL NULL NULL 600 60
Compare it to the next query, that groups data by a single column. Also auxiliary table @origs
is joined after data is grouped.
;with items as (
select
case grouping(id) when 0 then max(id) else NULL end id,
case grouping(id) when 0 then max(orig_id) else NULL end orig_id,
case grouping(id) when 0 then max(column1) else NULL end column1,
case grouping(id) when 0 then max(column2) else NULL end column2,
val1 = sum(value1),
val2 = sum(value2)
from @items
group by rollup (id)
)
select i.id, o.name as orig, i.column1, i.column2, i.val1, i.val2
from items i
left join @origs o on o.id = i.orig_id;
Output is the same:
id orig column1 column2 val1 val2
----- ----- -------- -------- ----- -----
1 orig1 c1.1 c2.1 100 10
2 orig1 c1.2 c2.2 200 20
3 orig2 c1.3 c2.3 300 30
NULL NULL NULL NULL 600 60
Upvotes: 1
Reputation: 5202
Given only a few thousand rows, I'd use a stored procedure to get the results without the grand totals into a temp table or table valued variable, and then return the results as a UNION ALL
of that table plus a grand total over the top of it.
Upvotes: 0