单次扫描中的数据行和总计行
Data rows plus totals row in a single scan
假设我们在 table 中有以下数据:
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
但是这样 table 被扫描了两次。
为了避免扫描数据两次,我尝试使用 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
这样只有一个 table 扫描,我几乎得到了我需要的,除了样本数据的前两行(重复的)合并为一个,我需要重复项保留为单独的行。
问题:
是否可以获取添加了总计行的 table 数据,以便扫描一次 table 数据并将重复项保留为单独的行?
所需的结果是 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
即使您认为您有重复的行,您也可以使它们独一无二并解决您的问题。一种方法是使用 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];
会给你:
与您最初的查询完全相同:
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
如果比较执行计划,您可以看到只执行了一次 table 扫描:
假设我们在 table 中有以下数据:
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
但是这样 table 被扫描了两次。
为了避免扫描数据两次,我尝试使用 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
这样只有一个 table 扫描,我几乎得到了我需要的,除了样本数据的前两行(重复的)合并为一个,我需要重复项保留为单独的行。
问题: 是否可以获取添加了总计行的 table 数据,以便扫描一次 table 数据并将重复项保留为单独的行?
所需的结果是 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
即使您认为您有重复的行,您也可以使它们独一无二并解决您的问题。一种方法是使用 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];
会给你:
与您最初的查询完全相同:
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
如果比较执行计划,您可以看到只执行了一次 table 扫描: