数据行加上总计在单次扫描中排

时间:2015-07-21 08:17:33

标签: sql-server tsql sql-server-2012 grouping-sets

想象一下,我们在表格中有以下数据:

groupName  volume  class  mark
---------- ------- ------ ----
group1     50      1      o
group1     50      1      o
group1     50      1      x
group1     25      2      o
group2     25      1      x
group2     17      3      x
group2     11      2      o
group3     11      1      o
group3     19      3      x

最后需要添加总计行(SUM表示卷,NULL表示其他列。

我知道我需要的是通过将union all添加为:

来实现
select 0 as isTotal, groupName, class, mark, volume
from dataTable
union all
select 1, NULL, NULL, NULL, sum(volume)
from dataTable
order by isTotal, groupName, class

但这种表格会被扫描两次。

为避免两次扫描数据,我尝试使用group by

select grouping(groupName) as isTotal, groupName, class, mark, sum(volume) as volume
from dataTable
group by grouping sets ((), (groupName, class, mark, volume))
order by isTotal, groupName, class

这种方式只有一个表扫描,我几乎得到了我需要的东西,除了样本数据的两个第一行(它们是重复的)合并为一个,我需要将重复项保存为单独的行

问题: 是否可以获取添加了总计行的表数据,以便扫描表数据一次,并将重复数据保存为单独的行?

期望的结果是union all查询返回的内容:

isTotal groupName   class  mark volume
------- ----------- ------ ---- -------
0       group 1     1      o    50
0       group 1     1      o    50
0       group 1     1      x    50
0       group 1     2      o    25
0       group 2     1      x    25
0       group 2     2      o    11
0       group 2     3      x    17
0       group 3     1      o    11
0       group 3     3      x    19
1       NULL        NULL   NULL 258

group by grouping sets查询返回的结果:

isTotal groupName  class  mark volume
------- ---------- ------ ---- -------
0       group 1    1      o    100
0       group 1    1      x    50
0       group 1    2      o    25
0       group 2    1      x    25
0       group 2    2      o    11
0       group 2    3      x    17
0       group 3    1      o    11
0       group 3    3      x    19
1       NULL       NULL   NULL 258

1 个答案:

答案 0 :(得分:2)

即使您认为自己有重复的行,也可以让它们与众不同并解决您的问题。一种方法是使用ROW_NUMBER函数。

例如:

DECLARE @DataSource TABLE
(
    [groupName] VARCHAR(6)
   ,[volume] TINYINT
   ,[class] TINYINT
   ,[mark] CHAR(1)
);

INSERT INTO @DataSource ([groupName], [volume], [class], [mark])
VALUES ('group1', '50', '1', 'x')
      ,('group1', '50', '1', 'x')
      ,('group1', '50', '1', 'o')
      ,('group1', '25', '2', 'o')
      ,('group2', '25', '1', 'x')
      ,('group2', '17', '3', 'x')
      ,('group2', '11', '2', 'o')
      ,('group3', '11', '1', 'o')
      ,('group3', '19', '3', 'x');

WITH DataSource ([rowID], [groupName], [volume], [class], [mark]) AS
(
    SELECT ROW_NUMBER() OVER(ORDER BY (SELECT 1))
          ,[groupName]
          ,[volume]
          ,[class]
          ,[mark]
    FROM @DataSource
)
SELECT GROUPING([groupName]) as [isTotal]
      ,[groupName]
      ,[class]
      ,[mark]
      ,SUM([volume]) AS [volume]
FROM DataSource
GROUP BY GROUPING SETS ((), ([rowID], [groupName], [volume], [class], [mark]))
ORDER BY [isTotal]
        ,[groupName]
        ,[class];

会给你:

enter image description here

与初始查询完全相同:

select 0 as isTotal, groupName, class, mark, volume
from @DataSource
union all
select 1, NULL, NULL, NULL, sum(volume)
from @DataSource
order by isTotal, groupName, class

如果比较执行计划,则只能看到一个表扫描:

enter image description here