Using pivot table with column and row totals in sql server 2008

user1931944 picture user1931944 · Jun 17, 2013 · Viewed 40.2k times · Source

I have a table with following columns

defect_id, developer_name, status, summary, root_cause, 
Secondary_RC, description, Comments, environment_name

The column root_cause has Enviro, Requi, Dev, TSc, TD, Unkn as its values and column environment_name has QA1, QA2, QA3

I need to prepare a report in the below format

    Enviro Requi  Dev TSc  TD Unkn  Total
QA1    9    1     14   17   2   3   46
QA2    8    1     14   0    5   1   29
QA3    1    1      7   0    0   1   10
Total 18    3     35   17   7   5   85

I have prepare the report till

    Enviro Requi  Dev  TSc  TD Unkn 
QA1    9    1     14    17   2  3   
QA2    8    1     14    0    5  1   
QA3    1    1      7    0    0  1   

I used the below query to get the above result

select *
from
(
  select environment_name as " ", value
  from test1 
  unpivot
  (
     value
    for col in (root_cause)
  ) unp
) src
pivot
(
  count(value)
  for value in ([Enviro] , [Requi] , [Dev] , [Tsc], [TD] , [Unkn])
) piv

Can anyone help to get the totals for columns and rows?

Answer

Andriy M picture Andriy M · Jun 17, 2013

There may be various approaches to this. You can calculate all the totals after the pivot, or you can get the totals first, then pivot all the results. It is also possible to have kind of middle ground: get one kind of the totals (e.g. the row-wise ones), pivot, then get the other kind, although that might be overdoing it.

The first of the mentioned approaches, getting all the totals after the pivot, could be done in a very straightforward way, and the only thing potentially new to you in the below implementation might be GROUP BY ROLLUP():

SELECT
  [ ]      = ISNULL(environment_name, 'Total'),
  [Enviro] = SUM([Enviro]),
  [Requi]  = SUM([Requi]),
  [Dev]    = SUM([Dev]),
  [Tsc]    = SUM([Tsc]),
  [TD]     = SUM([TD]),
  [Unkn]   = SUM([Unkn]),
  Total    = SUM([Enviro] + [Requi] + [Dev] + [Tsc] + [TD] + [Unkn])
FROM (
  SELECT environment_name, root_cause
  FROM test1
) s
PIVOT (
  COUNT(root_cause)
  FOR root_cause IN ([Enviro], [Requi], [Dev], [Tsc], [TD], [Unkn])
) p
GROUP BY
  ROLLUP(environment_name)
;

Basically, the GROUP BY ROLLUP() part produces the Total row for you. The grouping is first done by environment_name, then the grand total row is added.

To do just the opposite, i.e. get the totals prior to pivoting, you could employ GROUP BY CUBE() like this:

SELECT
  [ ]      = environment_name,
  [Enviro] = ISNULL([Enviro], 0),
  [Requi]  = ISNULL([Requi] , 0),
  [Dev]    = ISNULL([Dev]   , 0),
  [Tsc]    = ISNULL([Tsc]   , 0),
  [TD]     = ISNULL([TD]    , 0),
  [Unkn]   = ISNULL([Unkn]  , 0),
  Total    = ISNULL(Total   , 0)
FROM (
  SELECT
    environment_name = ISNULL(environment_name, 'Total'),
    root_cause       = ISNULL(root_cause,       'Total'),
    cnt              = COUNT(*)
  FROM test1
  WHERE root_cause IS NOT NULL
  GROUP BY
    CUBE(environment_name, root_cause)
) s
PIVOT (
  SUM(cnt)
  FOR root_cause IN ([Enviro], [Requi], [Dev], [Tsc], [TD], [Unkn], Total)
) p
;

Both methods can be tested and played with at SQL Fiddle:

Note. I've omitted the unpivoting step in both suggestions because unpivoting a single column seemed clearly redundant. If there's more to it, though, adjusting either of the queries should be easy.