具有多列聚合的 SQL Server 数据透视表
Posted
技术标签:
【中文标题】具有多列聚合的 SQL Server 数据透视表【英文标题】:SQL Server Pivot Table with multiple column aggregates 【发布时间】:2013-01-19 14:55:32 【问题描述】:我有一张桌子:
create table mytransactions(country varchar(30), totalcount int, numericmonth int, chardate char(20), totalamount money)
该表有以下记录:
insert into mytransactions(country, totalcount, numericmonth, chardate, totalamount) values('Australia', 36, 7, 'Jul-12', 699.96)
Go
insert into mytransactions(country, totalcount, numericmonth, chardate, totalamount) values('Australia', 44, 8, 'Aug-12', 1368.71)
Go
insert into mytransactions(country, totalcount, numericmonth, chardate, totalamount) values('Australia', 52, 9, 'Sep-12', 1161.33)
Go
insert into mytransactions(country, totalcount, numericmonth, chardate, totalamount) values('Australia', 50, 10, 'Oct-12', 1099.84)
Go
insert into mytransactions(country, totalcount, numericmonth, chardate, totalamount) values('Australia', 38, 11, 'Nov-12', 1078.94)
Go
insert into mytransactions(country, totalcount, numericmonth, chardate, totalamount) values('Australia', 63, 12, 'Dec-12', 1668.23)
Go
insert into mytransactions(country, totalcount, numericmonth, chardate, totalamount) values('Austria', 11, 7, 'Jul-12', 257.82)
Go
insert into mytransactions(country, totalcount, numericmonth, chardate, totalamount) values('Austria', 5, 8, 'Aug-12', 126.55)
Go
insert into mytransactions(country, totalcount, numericmonth, chardate, totalamount) values('Austria', 7, 9, 'Sep-12', 92.11)
Go
insert into mytransactions(country, totalcount, numericmonth, chardate, totalamount) values('Austria', 12, 10, 'Oct-12', 103.56)
Go
insert into mytransactions(country, totalcount, numericmonth, chardate, totalamount) values('Austria', 21, 11, 'Nov-12', 377.68)
Go
insert into mytransactions(country, totalcount, numericmonth, chardate, totalamount) values('Austria', 3, 12, 'Dec-12', 14.35)
Go
这是 select * 的样子:
Country TotalCount numericmonth chardate totalamount
--------- ---------- ----------- -------- -----------
Australia 36 7 Jul-12 699.96
Australia 44 8 Aug-12 1368.71
Australia 52 9 Sep-12 1161.33
Australia 50 10 Oct-12 1099.84
Australia 38 11 Nov-12 1078.94
Australia 63 12 Dec-12 1668.23
Austria 11 7 Jul-12 257.82
Austria 5 8 Aug-12 126.55
Austria 7 9 Sep-12 92.11
Austria 12 10 Oct-12 103.56
Austria 21 11 Nov-12 377.68
Austria 3 12 Dec-12 14.35
我想旋转这个记录集,使它看起来像这样:
Australia Australia Austria Austria
# of Transactions Total $ amount # of Transactions Total $ amount
----------------- -------------- ----------------- --------------
Jul-12 36 699.96 11 257.82
Aug-12 44 1368.71 5 126.55
Sep-12 52 1161.33 7 92.11
Oct-12 50 1099.84 12 103.56
Nov-12 38 1078.94 21 377.68
Dec-12 63 1668.23 3 14.35
这是我迄今为止提出的枢轴代码:
select * from mytransactions
pivot (sum (totalcount) for country in ([Australia], [Austria])) as pvt
这就是我得到的:
numericmonth chardate totalamount Australia Austria
----------- -------- ---------- --------- -------
7 Jul-12 257.82 NULL 11
7 Jul-12 699.96 36 NULL
8 Aug-12 126.55 NULL 5
8 Aug-12 1368.71 44 NULL
9 Sep-12 92.11 NULL 7
9 Sep-12 1161.33 52 NULL
10 Oct-12 103.56 NULL 12
10 Oct-12 1099.84 50 NULL
11 Nov-12 377.68 NULL 21
11 Nov-12 1078.94 38 NULL
12 Dec-12 14.35 NULL 3
12 Dec-12 1668.23 63 NULL
我可以手动聚合表变量循环中的记录,但似乎枢轴可能能够做到这一点。
是否可以使用 pivot 获得我想要的记录集,或者是否有其他我不知道的工具?
谢谢
【问题讨论】:
国家列表是静态的(只有澳大利亚和奥地利)? 不,但我不想让问题变得复杂。如果我能得到这两个国家的答案,我可以使查询动态化。 解决方案!!!使用这个***.com/questions/15274305/… 【参考方案1】:我会通过同时应用UNPIVOT
和PIVOT
函数来获得最终结果来做这件事。 unpivot 从totalcount
和totalamount
列中获取值,并将它们放入具有多行的一列中。然后,您可以以这些结果为中心。:
select chardate,
Australia_totalcount as [Australia # of Transactions],
Australia_totalamount as [Australia Total $ Amount],
Austria_totalcount as [Austria # of Transactions],
Austria_totalamount as [Austria Total $ Amount]
from
(
select
numericmonth,
chardate,
country +'_'+col col,
value
from
(
select numericmonth,
country,
chardate,
cast(totalcount as numeric(10, 2)) totalcount,
cast(totalamount as numeric(10, 2)) totalamount
from mytransactions
) src
unpivot
(
value
for col in (totalcount, totalamount)
) unpiv
) s
pivot
(
sum(value)
for col in (Australia_totalcount, Australia_totalamount,
Austria_totalcount, Austria_totalamount)
) piv
order by numericmonth
见SQL Fiddle with Demo。
如果您有未知数量的country
名称,那么您可以使用动态 SQL:
DECLARE @cols AS NVARCHAR(MAX),
@colsName AS NVARCHAR(MAX),
@query AS NVARCHAR(MAX)
select @cols = STUFF((SELECT distinct ',' + QUOTENAME(country +'_'+c.col)
from mytransactions
cross apply
(
select 'TotalCount' col
union all
select 'TotalAmount'
) c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
select @colsName
= STUFF((SELECT distinct ', ' + QUOTENAME(country +'_'+c.col)
+' as ['
+ country + case when c.col = 'TotalCount' then ' # of Transactions]' else 'Total $ Amount]' end
from mytransactions
cross apply
(
select 'TotalCount' col
union all
select 'TotalAmount'
) c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set @query
= 'SELECT chardate, ' + @colsName + '
from
(
select
numericmonth,
chardate,
country +''_''+col col,
value
from
(
select numericmonth,
country,
chardate,
cast(totalcount as numeric(10, 2)) totalcount,
cast(totalamount as numeric(10, 2)) totalamount
from mytransactions
) src
unpivot
(
value
for col in (totalcount, totalamount)
) unpiv
) s
pivot
(
sum(value)
for col in (' + @cols + ')
) p
order by numericmonth'
execute(@query)
见SQL Fiddle with Demo
两者都给出结果:
| CHARDATE | AUSTRALIA # OF TRANSACTIONS | AUSTRALIA TOTAL $ AMOUNT | AUSTRIA # OF TRANSACTIONS | AUSTRIA TOTAL $ AMOUNT |
--------------------------------------------------------------------------------------------------------------------------------------
| Jul-12 | 36 | 699.96 | 11 | 257.82 |
| Aug-12 | 44 | 1368.71 | 5 | 126.55 |
| Sep-12 | 52 | 1161.33 | 7 | 92.11 |
| Oct-12 | 50 | 1099.84 | 12 | 103.56 |
| Nov-12 | 38 | 1078.94 | 21 | 377.68 |
| Dec-12 | 63 | 1668.23 | 3 | 14.35 |
【讨论】:
好吧,你必须承认你的大脑会做一些奇怪的事情......但它确实提供了解决方案。 虽然我喜欢PIVOT
,但我不认为它在这种情况下有一些好处(性能、可读性)。
@BogdanSahlean 如果他们需要动态版本,那么PIVOT
将是最好的
@bluefeet 为什么当我将 charDate 与国家/地区交换时,我得到了所需的列,但也得到了相同的行数,而不是奥地利、澳大利亚的两行。这是我所做的sqlfiddle.com/#!3/4bd75/397
@kuklei 子查询应该只包括最终显示或枢轴所需的列 - 所以删除numericmonth
-- sqlfiddle.com/#!3/4bd75/398【参考方案2】:
我使用您自己的数据透视表作为嵌套查询并得出以下结果:
SELECT
[sub].[chardate],
SUM(ISNULL([Australia], 0)) AS [Transactions Australia],
SUM(CASE WHEN [Australia] IS NOT NULL THEN [TotalAmount] ELSE 0 END) AS [Amount Australia],
SUM(ISNULL([Austria], 0)) AS [Transactions Austria],
SUM(CASE WHEN [Austria] IS NOT NULL THEN [TotalAmount] ELSE 0 END) AS [Amount Austria]
FROM
(
select *
from mytransactions
pivot (sum (totalcount) for country in ([Australia], [Austria])) as pvt
) AS [sub]
GROUP BY
[sub].[chardate],
[sub].[numericmonth]
ORDER BY
[sub].[numericmonth] ASC
Here is the Fiddle.
【讨论】:
“很多其他的都好得多!” (现实生活中的很多其他人):-D 这个解决方案给一个相当简单的查询增加了不必要的复杂度。 一开始没看到这个,但不得不承认你是对的。也为你 +1。【参考方案3】:最简单、最直接的方法是简单地将主查询与数据透视表包装在一个公用表表达式中,然后进行分组/聚合。
WITH PivotCTE AS
(
select * from mytransactions
pivot (sum (totalcount) for country in ([Australia], [Austria])) as pvt
)
SELECT
numericmonth,
chardate,
SUM(totalamount) AS totalamount,
SUM(ISNULL(Australia, 0)) AS Australia,
SUM(ISNULL(Austria, 0)) Austria
FROM PivotCTE
GROUP BY numericmonth, chardate
ISNULL
用于阻止 NULL
值使总和无效(因为 NULL
+ 任何值 = NULL
)
【讨论】:
以上是关于具有多列聚合的 SQL Server 数据透视表的主要内容,如果未能解决你的问题,请参考以下文章