【问题标题】:Using pivot table with column and row totals in sql server 2008在 sql server 2008 中使用带有列和行总计的数据透视表
【发布时间】:2013-06-12 23:54:19
【问题描述】:

我有一个包含以下列的表格

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

root_cause 列的值是 Enviro、Requi、Dev、TSc、TD、Unkn,并且 列 environment_name 有 QA1、QA2、QA3

我需要准备一份如下格式的报告

    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

我已经准备好报告了

    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   

我使用下面的查询得到了上面的结果

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

任何人都可以帮助获取列和行的总数吗?

【问题讨论】:

  • 欢迎来到 StackOverflow:如果您发布代码、XML 或数据示例,在文本编辑器中突出显示这些行并单击“代码示例”按钮 ({ } ) 在编辑器工具栏上以很好地格式化和语法突出显示它!
  • 此查询中unpivot 部分的用途是什么?当然,您可以只用select environment_name as " ", root_cause as value from test1 替换整个子查询,不是吗?
  • 对不起,我的意思是你可以这样重写它。

标签: sql sql-server-2008 pivot


【解决方案1】:

对此可能有多种方法。您可以在透视后计算所有总计,也可以先获取总计,然后透视所有结果。也有可能有一种中间立场:获取一种总数(例如,按行计算),旋转,然后获取另一种,尽管这可能有点过头了。

上述方法中的第一种,在数据透视后获取所有总数,可以通过一种非常简单的方式完成,在下面的实现中,唯一对您来说可能是新的可能是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)
;

基本上,GROUP BY ROLLUP() 部分会为您生成 Total row。分组首先由environment_name 完成,然后添加总计行。

要反其道而行之,即在旋转之前获取总数,您可以像这样使用GROUP BY CUBE()

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
;

这两种方法都可以在 SQL Fiddle 进行测试和使用:

注意。我在这两个建议中都省略了反透视步骤,因为反透视单个列似乎显然是多余的。不过,如果还有更多内容,调整其中一个查询应该很容易。

【讨论】:

  • 对于方法1和MSSQL 2005的小加点(是的,现在还在某处使用..)sql2005说没有这个ROLLUP函数,解决方法是替换“GROUP BY ROLLUP(environment_name)”使用“GROUP BY environment_name WITH ROLLUP”也适用于 Sql2008。也不是很清楚为什么 FROM 中有子查询 - 而不是“FROM (..) s”可以只有“FROM test1”。无论如何+1,非常有帮助!
  • @sarh: Re: 为什么子查询——如果它只是FROM test1 PIVOT ...,那么 PIVOT 将使用test1 中的所有列(除了指定为聚合函数参数的列)作为分组标准,但我们只需要按environment_name分组即可。
  • @AndriyM:我不确定我是否理解.. 我在您的示例解决方案中添加了一列,用一些数字填充它,在没有子查询的情况下运行并得到相同的结果:sqlfiddle.com/#!3/8fe8c/1
  • @sarh:确实你是对的。不同之处仅在于中间 PIVOT 结果集:没有子查询could potentially be multiple entries with same environment_name。但是GROUP BY ROLLUP(environment_name) 最终会抑制它们并聚合计数,产生与子查询相同的输出。
【解决方案2】:

您可以使用 ROLLUP 找到 root_causeenvironment_name 的总计。

  • RNO_COLTOTAL - 将Total 放在最后一列的逻辑,因为Tsc,Unkn 列将在旋转时与Total 列重叠,因为它按字母顺序排列。
  • RNO_ROWTOTAL - 逻辑将 Total 放在最后一行,因为以 U,W,X,Y,Z 开头的值可以与值 Total 重叠,因为它的排序按字母顺序。
  • SUM(VALUE) - 可以定义我们可以与 ROLLUP 一起使用的聚合函数。

查询 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

使用CUBE时我们会得到如下逻辑

我们声明用于旋转的变量。

  • @cols - 用于透视的列值。
  • @NulltoZeroCols - 用零替换空值。

查询 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) 

现在动态旋转它

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

结果

【讨论】:

    【解决方案3】:

    我认为您需要单独计算总计。使用这个简单的查询总数(抱歉,必须为您的“”列提供别名):

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

    您可以轻松地将两个查询连接在一起以获得所需的报告:

    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 
    

    我没有测试过,但相信它会起作用

    【讨论】:

    • 谢谢你,它解决了总行数。我仍在尝试总计列
    • 什么意思?计算不正确吗?
    • 它只计算单独的行总计,但我需要和 Cha 一样的列总计。
    • 我明白你的意思。你用工会做这种类型的把戏。给我一秒钟,我会更新答案
    • 感谢 Cha,但在 b 处运行查询时出错。它期待 a 和 b 之间的某些条件
    【解决方案4】:

    您也可以将不带总计的 select pivot 放在临时表中,然后使用新的 select 添加 totalS:

    SELECT  environment_name=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 #temp
        GROUP BY ROLLUP(environment_name)
    

    【讨论】:

      猜你喜欢
      • 2012-11-18
      • 2012-11-05
      • 2012-03-24
      • 1970-01-01
      • 2023-04-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-27
      相关资源
      最近更新 更多