SQL 服务器中动态枢轴和静态枢轴的组合
Combination of dynamic pivot and static pivot in SQL Server
动态枢轴与静态聚合相结合
我有一个看起来像这样的 table:
Place State Category CategCount MCount Buys Cost
London UK Old 3 NULL 22 4.50
London UK Old 6 5 3 22.00
Brussels BE Young 2 NULL 4 3.50
Brussels BE M NULL 5 12 1.20
Brussels BE M NULL 2 1 1.20
我基本上需要:
- 按多个字段(示例中的地点、州、类别)分组
- 每个这样的组计数
- 每组求和 MCount、成本(以及其他,不在示例中),这些列是静态的
- 对类别进行透视并对每个此类分组类别(此处为旧的、年轻的)求和 CategCount。这是动态部分
结果应如下所示:
Count Place State Category SumMCount SumOld SumYoung SumCost SumBuys
2 London UK Old 5 9 0 26.50 25
1 Brussels BE Young 0 0 2 3.50 4
2 Brussels BE NULL 7 0 0 2.40 13
我知道如何获取动态数据透视查询(根据 )并且我知道如何执行静态部分。 但我不知道如何将两者结合起来。有人有什么想法吗?也许我做错了?
到目前为止我得到了什么:
下面给出了 Old
和 Young
的正确动态数据透视结果,但不确定如何添加计数和 'regular' sums/aggregates :
create table #temp
(
Place nvarchar(20),
State nvarchar(20),
Category nvarchar(20) null,
CategCount int null,
MCount int null,
Buys int,
Cost int
)
insert into #temp values ('London', 'UK', 'Old', 3, NULL, 22, 4.50)
insert into #temp values ('London', 'UK', 'Old', 6, 5, 3, 22.00)
insert into #temp values ('Brussels', 'BE', 'Young', 2, NULL, 4, 3.50)
insert into #temp values ('Brussels', 'BE', 'M', NULL, 5, 12, 1.20)
insert into #temp values ('Brussels', 'BE', 'M', NULL, 2, 1, 1.20)
DECLARE @cols AS NVARCHAR(MAX)='';
DECLARE @query AS NVARCHAR(MAX)='';
SELECT @cols = @cols + QUOTENAME(Category) + ',' FROM (select distinct Category from #temp where CategCount IS NOT NULL) as tmp
select @cols = substring(@cols, 0, len(@cols)) --trim "," at end
--select (@cols) as bm
set @query =
'SELECT * from
(
select
sum(CategCount) as totalCatCount,
Category
from #temp
group by Category
) src
pivot
(
max(totalCatCount) for Category in (' + @cols + ')
) piv'
execute(@query)
drop table #temp
返回:
下面是没有旋转的 'regular' 查询:
select count(*) as count, place, state, category,
sum(ISNULL(CategCount, 0)) as SumCatCount,
sum(ISNULL(MCount, 0)) as SumMCount,
sum(ISNULL(buys, 0)) as SumBuys,
sum(Cost) as SumCost
from #temp
group by place, state, category
返回:
但它应该看起来像这样:
我已将查询的静态数据透视部分用作动态数据透视的来源。创建两组动态数据透视表列列表。一个用于旋转,另一个使用 Coalesce() 到 select 旋转列(将 null 转换为 0)。如果任何类别都没有 categcount,则该类别已被替换为 null(case when)。 Category 和 SumCatCount 的另外两个别名已在数据透视条件中使用后创建。
这是你的答案:
create table #temp
(
Place nvarchar(20),
State nvarchar(20),
Category nvarchar(20) null,
CategCount int null,
MCount int null,
Buys int,
Cost int
)
insert into #temp values ('London', 'UK', 'Old', 3, NULL, 22, 4.50)
insert into #temp values ('London', 'UK', 'Old', 6, 5, 3, 22.00)
insert into #temp values ('Brussels', 'BE', 'Young', 2, NULL, 4, 3.50)
insert into #temp values ('Brussels', 'BE', 'M', NULL, 5, 12, 1.20)
insert into #temp values ('Brussels', 'BE', 'M', NULL, 2, 1, 1.20)
DECLARE @cols AS NVARCHAR(MAX)='';
DECLARE @query AS NVARCHAR(MAX)='';
DECLARE @colsForSelect AS NVARCHAR(MAX)='';
SET @cols = STUFF((SELECT distinct ',' + quotename(category)
FROM #temp where CategCount is not null
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET @colsForSelect = STUFF((SELECT distinct ',' + ' Coalesce('+quotename(category)+',0) '+ quotename(category)
FROM #temp where CategCount is not null
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
--select (@cols) as bm
set @query =
'SELECT count,place,state,(case when OldSumCatCount >0 then OldCategory else null end)Category,SumMCount, ' + @colsForSelect + ',SumCost,SumBuys from
(
select count(*) as count, place, state,category OldCategory, category,
sum(ISNULL(MCount, 0)) as SumMCount,
sum(ISNULL(CategCount, 0)) as OldSumCatCount,
sum(ISNULL(CategCount, 0)) as SumCatCount,
sum(Cost) as SumCost,
sum(ISNULL(buys, 0)) as SumBuys
from #temp
group by place , state, category
) src
pivot
(
max(SumCatCount) for Category in (' + @cols + ')
) piv
order by place desc,count'
execute(@query)
GO
count
place
state
Category
SumMCount
Old
Young
SumCost
SumBuys
2
London
UK
Old
5
9
0
26
25
1
Brussels
BE
Young
0
0
2
3
4
2
Brussels
BE
null
7
0
0
2
13
db<>fiddle here
感谢@Larnu 在评论中为我指明了正确的方向。 His/her 关于“您不能将静态查询连接到动态查询”的声明,以及要么全有要么全无必须是动态的,促使我构建动态部分并简单地扩展它。
我想我需要以某种方式重复 PIVOT
部分中的列,但事实并非如此。显然,只有你想旋转的列(逻辑上是这样,一旦你想到它)。
我还没有弄清楚的唯一部分是如何摆脱结果集中的 NULL
,希望有人能记住这一点 ;)。
DECLARE @cols AS NVARCHAR(MAX)='';
DECLARE @query AS NVARCHAR(MAX)='';
SELECT @cols = @cols + QUOTENAME(Category) + ',' FROM (select distinct Category from #temp where CategCount IS NOT NULL) as tmp
select @cols = substring(@cols, 0, len(@cols)) --trim "," at end
--select (@cols) as bm
set @query =
'SELECT * from
(
select
count(*) as count,
Place,
State,
Category,
Category as CatPivot,
sum(ISNULL(CategCount, 0)) as TotalCatCount,
sum(ISNULL(Buys, 0)) as SumBuys,
sum(ISNULL(Cost, 0)) as SumCost,
sum(ISNULL(MCount, 0)) as SumMCount
from #temp
group by Category, Place, State
) src
pivot
(
max(TotalCatCount) for CatPivot in (' + @cols + ')
) piv'
execute(@query)
在这里,我分享了另一个相同的答案,但正如@Anthony Hancock 所建议的那样,已经使用 string_agg() 而不是 stuff() 和 xml 路径创建了数据透视表的动态列名称().它的速度和可读性都太快了(对于 SQL Server 2017 及更高版本)
DECLARE @cols AS NVARCHAR(MAX)='';
DECLARE @query AS NVARCHAR(MAX)='';
DECLARE @colsForSelect AS NVARCHAR(MAX)='';
select @cols =string_agg(category,',') from (
select distinct category FROM #temp where CategCount is not null )t
select @colsForSelect= STRING_AGG(category,',') from
(select distinct 'coalesce('+category+',0) '+category category FROM #temp where CategCount is not null )t
set @query =
'SELECT count,place,state,(case when OldSumCatCount >0 then OldCategory else null end)Category,SumMCount, ' + @colsForSelect + ',SumCost,SumBuys from
(
select count(*) as count, place, state,category OldCategory, category,
sum(ISNULL(MCount, 0)) as SumMCount,
sum(ISNULL(CategCount, 0)) as OldSumCatCount,
sum(ISNULL(CategCount, 0)) as SumCatCount,
sum(Cost) as SumCost,
sum(ISNULL(buys, 0)) as SumBuys
from #temp
group by place , state, category
) src
pivot
(
max(SumCatCount) for Category in (' + @cols + ')
) piv
order by place desc,count'
execute(@query)
动态枢轴与静态聚合相结合
我有一个看起来像这样的 table:
Place State Category CategCount MCount Buys Cost
London UK Old 3 NULL 22 4.50
London UK Old 6 5 3 22.00
Brussels BE Young 2 NULL 4 3.50
Brussels BE M NULL 5 12 1.20
Brussels BE M NULL 2 1 1.20
我基本上需要:
- 按多个字段(示例中的地点、州、类别)分组
- 每个这样的组计数
- 每组求和 MCount、成本(以及其他,不在示例中),这些列是静态的
- 对类别进行透视并对每个此类分组类别(此处为旧的、年轻的)求和 CategCount。这是动态部分
结果应如下所示:
Count Place State Category SumMCount SumOld SumYoung SumCost SumBuys
2 London UK Old 5 9 0 26.50 25
1 Brussels BE Young 0 0 2 3.50 4
2 Brussels BE NULL 7 0 0 2.40 13
我知道如何获取动态数据透视查询(根据 )并且我知道如何执行静态部分。 但我不知道如何将两者结合起来。有人有什么想法吗?也许我做错了?
到目前为止我得到了什么:
下面给出了 Old
和 Young
的正确动态数据透视结果,但不确定如何添加计数和 'regular' sums/aggregates :
create table #temp
(
Place nvarchar(20),
State nvarchar(20),
Category nvarchar(20) null,
CategCount int null,
MCount int null,
Buys int,
Cost int
)
insert into #temp values ('London', 'UK', 'Old', 3, NULL, 22, 4.50)
insert into #temp values ('London', 'UK', 'Old', 6, 5, 3, 22.00)
insert into #temp values ('Brussels', 'BE', 'Young', 2, NULL, 4, 3.50)
insert into #temp values ('Brussels', 'BE', 'M', NULL, 5, 12, 1.20)
insert into #temp values ('Brussels', 'BE', 'M', NULL, 2, 1, 1.20)
DECLARE @cols AS NVARCHAR(MAX)='';
DECLARE @query AS NVARCHAR(MAX)='';
SELECT @cols = @cols + QUOTENAME(Category) + ',' FROM (select distinct Category from #temp where CategCount IS NOT NULL) as tmp
select @cols = substring(@cols, 0, len(@cols)) --trim "," at end
--select (@cols) as bm
set @query =
'SELECT * from
(
select
sum(CategCount) as totalCatCount,
Category
from #temp
group by Category
) src
pivot
(
max(totalCatCount) for Category in (' + @cols + ')
) piv'
execute(@query)
drop table #temp
返回:
下面是没有旋转的 'regular' 查询:
select count(*) as count, place, state, category,
sum(ISNULL(CategCount, 0)) as SumCatCount,
sum(ISNULL(MCount, 0)) as SumMCount,
sum(ISNULL(buys, 0)) as SumBuys,
sum(Cost) as SumCost
from #temp
group by place, state, category
返回:
但它应该看起来像这样:
我已将查询的静态数据透视部分用作动态数据透视的来源。创建两组动态数据透视表列列表。一个用于旋转,另一个使用 Coalesce() 到 select 旋转列(将 null 转换为 0)。如果任何类别都没有 categcount,则该类别已被替换为 null(case when)。 Category 和 SumCatCount 的另外两个别名已在数据透视条件中使用后创建。
这是你的答案:
create table #temp
(
Place nvarchar(20),
State nvarchar(20),
Category nvarchar(20) null,
CategCount int null,
MCount int null,
Buys int,
Cost int
)
insert into #temp values ('London', 'UK', 'Old', 3, NULL, 22, 4.50)
insert into #temp values ('London', 'UK', 'Old', 6, 5, 3, 22.00)
insert into #temp values ('Brussels', 'BE', 'Young', 2, NULL, 4, 3.50)
insert into #temp values ('Brussels', 'BE', 'M', NULL, 5, 12, 1.20)
insert into #temp values ('Brussels', 'BE', 'M', NULL, 2, 1, 1.20)
DECLARE @cols AS NVARCHAR(MAX)='';
DECLARE @query AS NVARCHAR(MAX)='';
DECLARE @colsForSelect AS NVARCHAR(MAX)='';
SET @cols = STUFF((SELECT distinct ',' + quotename(category)
FROM #temp where CategCount is not null
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET @colsForSelect = STUFF((SELECT distinct ',' + ' Coalesce('+quotename(category)+',0) '+ quotename(category)
FROM #temp where CategCount is not null
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
--select (@cols) as bm
set @query =
'SELECT count,place,state,(case when OldSumCatCount >0 then OldCategory else null end)Category,SumMCount, ' + @colsForSelect + ',SumCost,SumBuys from
(
select count(*) as count, place, state,category OldCategory, category,
sum(ISNULL(MCount, 0)) as SumMCount,
sum(ISNULL(CategCount, 0)) as OldSumCatCount,
sum(ISNULL(CategCount, 0)) as SumCatCount,
sum(Cost) as SumCost,
sum(ISNULL(buys, 0)) as SumBuys
from #temp
group by place , state, category
) src
pivot
(
max(SumCatCount) for Category in (' + @cols + ')
) piv
order by place desc,count'
execute(@query)
GO
count | place | state | Category | SumMCount | Old | Young | SumCost | SumBuys |
---|---|---|---|---|---|---|---|---|
2 | London | UK | Old | 5 | 9 | 0 | 26 | 25 |
1 | Brussels | BE | Young | 0 | 0 | 2 | 3 | 4 |
2 | Brussels | BE | null | 7 | 0 | 0 | 2 | 13 |
db<>fiddle here
感谢@Larnu 在评论中为我指明了正确的方向。 His/her 关于“您不能将静态查询连接到动态查询”的声明,以及要么全有要么全无必须是动态的,促使我构建动态部分并简单地扩展它。
我想我需要以某种方式重复 PIVOT
部分中的列,但事实并非如此。显然,只有你想旋转的列(逻辑上是这样,一旦你想到它)。
我还没有弄清楚的唯一部分是如何摆脱结果集中的 NULL
,希望有人能记住这一点 ;)。
DECLARE @cols AS NVARCHAR(MAX)='';
DECLARE @query AS NVARCHAR(MAX)='';
SELECT @cols = @cols + QUOTENAME(Category) + ',' FROM (select distinct Category from #temp where CategCount IS NOT NULL) as tmp
select @cols = substring(@cols, 0, len(@cols)) --trim "," at end
--select (@cols) as bm
set @query =
'SELECT * from
(
select
count(*) as count,
Place,
State,
Category,
Category as CatPivot,
sum(ISNULL(CategCount, 0)) as TotalCatCount,
sum(ISNULL(Buys, 0)) as SumBuys,
sum(ISNULL(Cost, 0)) as SumCost,
sum(ISNULL(MCount, 0)) as SumMCount
from #temp
group by Category, Place, State
) src
pivot
(
max(TotalCatCount) for CatPivot in (' + @cols + ')
) piv'
execute(@query)
在这里,我分享了另一个相同的答案,但正如@Anthony Hancock 所建议的那样,已经使用 string_agg() 而不是 stuff() 和 xml 路径创建了数据透视表的动态列名称().它的速度和可读性都太快了(对于 SQL Server 2017 及更高版本)
DECLARE @cols AS NVARCHAR(MAX)='';
DECLARE @query AS NVARCHAR(MAX)='';
DECLARE @colsForSelect AS NVARCHAR(MAX)='';
select @cols =string_agg(category,',') from (
select distinct category FROM #temp where CategCount is not null )t
select @colsForSelect= STRING_AGG(category,',') from
(select distinct 'coalesce('+category+',0) '+category category FROM #temp where CategCount is not null )t
set @query =
'SELECT count,place,state,(case when OldSumCatCount >0 then OldCategory else null end)Category,SumMCount, ' + @colsForSelect + ',SumCost,SumBuys from
(
select count(*) as count, place, state,category OldCategory, category,
sum(ISNULL(MCount, 0)) as SumMCount,
sum(ISNULL(CategCount, 0)) as OldSumCatCount,
sum(ISNULL(CategCount, 0)) as SumCatCount,
sum(Cost) as SumCost,
sum(ISNULL(buys, 0)) as SumBuys
from #temp
group by place , state, category
) src
pivot
(
max(SumCatCount) for Category in (' + @cols + ')
) piv
order by place desc,count'
execute(@query)