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

后端 未结 3 877
天命终不由人
天命终不由人 2020-12-09 05:36

I have a table with following columns

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


        
相关标签:
3条回答
  • 2020-12-09 05:50

    You can find Total for root_cause and environment_name using ROLLUP.

    • RNO_COLTOTAL - Logic to place Total in last column, since the columns Tsc,Unkn will overlap the column Total when pivoting, since its ordering alphabetically.
    • RNO_ROWTOTAL - Logic to place Total in last row since a value that is starting with U,W,X,Y,Z can overlap the value Total, since its ordering alphabetically.
    • SUM(VALUE) - Can define on what aggregate function we can use with ROLLUP.

    QUERY 1

    SELECT CASE WHEN root_cause IS NULL THEN 1 ELSE 0 END RNO_COLTOTAL, 
    CASE WHEN environment_name IS NULL THEN 1 ELSE 0 END RNO_ROWTOTAL,
    ISNULL(environment_name,'Total')environment_name,
    ISNULL(root_cause,'Total')root_cause,
    SUM(VALUE) VALUE
    INTO #NEWTABLE
    FROM
    (
        -- Find the count for environment_name,root_cause
        SELECT DISTINCT *,COUNT(*) OVER(PARTITION BY environment_name,root_cause)VALUE 
        FROM #TEMP
    )TAB
    GROUP BY root_cause,environment_name
    WITH CUBE
    

    We will get the following logic when CUBE is used

    enter image description here

    We declare variables for pivoting.

    • @cols - Column values for pivoting.
    • @NulltoZeroCols - Replace null values with zero.

    QUERY 2

    DECLARE @cols NVARCHAR (MAX)
    
    SELECT @cols = COALESCE (@cols + ',[' + root_cause + ']', 
                   '[' + root_cause + ']')
                   FROM    (SELECT DISTINCT RNO_COLTOTAL,root_cause FROM #NEWTABLE) PV 
                   ORDER BY  RNO_COLTOTAL,root_cause 
    
    DECLARE @NulltoZeroCols NVARCHAR (MAX)
    
    SET @NullToZeroCols = SUBSTRING((SELECT ',ISNULL(['+root_cause+'],0) AS ['+root_cause+']' 
    FROM(SELECT DISTINCT RNO_COLTOTAL,root_cause FROM #NEWTABLE GROUP BY RNO_COLTOTAL,root_cause)TAB  
    ORDER BY RNO_COLTOTAL  FOR XML PATH('')),2,8000) 
    

    Now pivot it dynamically

    DECLARE @query NVARCHAR(MAX)
    SET @query = 'SELECT environment_name,'+ @NulltoZeroCols +' FROM 
                 (
                     SELECT RNO_ROWTOTAL,environment_name,root_cause,VALUE
                     FROM #NEWTABLE
                 ) x
                 PIVOT 
                 (
                     MIN(VALUE)
                     FOR [root_cause] IN (' + @cols + ')
                ) p
                ORDER BY RNO_ROWTOTAL,environment_name;' 
    
    EXEC SP_EXECUTESQL @query
    

    RESULT

    enter image description here

    0 讨论(0)
  • 2020-12-09 05:52

    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:

    • Method 1

    • Method 2

    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.

    0 讨论(0)
  • 2020-12-09 05:55

    I think you need to calculate the Total separately. Using this simple query for the total (sorry, had to give the alias name for your " " column):

    select environment_name as en, 
    count (*) AS Total
    FROM test1 
    WHERE value in ('Enviro', 'Requi', 'Dev', 'Tsc', 'TD', 'Unkn')
    GROUP BY environment_name
    

    you can easily join both queries together to get the required report:

    SELECT * FROM
    (select *
    from
    (
      select environment_name as en, 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
    ) AS a 
    INNER JOIN
    ( select environment_name as en, 
      count (*) AS Total
      FROM test1 
      WHERE value in ('Enviro', 'Requi', 'Dev', 'Tsc', 'TD', 'Unkn')
      GROUP BY environment_name
     ) AS b ON a.en = b.en
    UNION ALL
    SELECT * FROM
    (select *
    from
    (
      select 'Total' as en, 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
    ) AS a 
    INNER JOIN
    ( select 'Total' as en, 
      count (*) AS Total
      FROM test1 
      WHERE value in ('Enviro', 'Requi', 'Dev', 'Tsc', 'TD', 'Unkn')
     ) AS b 
    

    I have not tested it, but believe it will work

    0 讨论(0)
提交回复
热议问题