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

我基本上需要:

结果应如下所示:

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

我知道如何获取动态数据透视查询(根据 )并且我知道如何执行静态部分。 但我不知道如何将两者结合起来。有人有什么想法吗?也许我做错了?

到目前为止我得到了什么:

下面给出了 OldYoung 的正确动态数据透视结果,但不确定如何添加计数和 '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)