Rambo3
Rambo3

Reputation: 391

Best way to obtain total summary row of query result

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

Answers (2)

i-one
i-one

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

dsz
dsz

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

Related Questions