在 SQL Server 中使用逐行总和进行透视[关闭]

Posted

技术标签:

【中文标题】在 SQL Server 中使用逐行总和进行透视[关闭]【英文标题】:Pivot with row wise Sum in SQL Server [closed] 【发布时间】:2021-03-24 06:51:06 【问题描述】:

表-A:

PID BrandName
1 Brand1
2 Brand2
3 Brand3
4 Brand4
5 Brand5
6 Brand6
7 Brand7
8 Brand8

表-B:

CustNo Name BrandName Qty Amt
1 C1 Brand1 3 300
1 C1 Brand2 2 400
1 C1 Brand4 1 300
1 C1 Brand5 2 100
2 C2 Brand1 2 200
2 C2 Brand3 1 200
3 C3 Brand2 1 300
3 C3 Brand7 3 150

预期结果:-

CustNo Name Brand1 Brand2 Brand3 Brand4 Brand5 Brand6 Brand7 Brand8 Amt
1 C1 3 2 0 1 2 0 0 0 1100
2 C2 2 0 1 0 0 0 0 0 400
3 C3 0 1 0 0 0 0 3 0 450

我试过的枢轴:-

DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX)

select @cols = STUFF((SELECT ',' + QUOTENAME(BrandName) from [TABLE-A] order by PID FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)') ,1,1,'')

set @query = 'SELECT CustNo,[Name],' + @cols + '
             from 
             (
select CustNo,[Name],Qty,SUM(cast([amt] as float)) as Amt,BrandName from [TABLE-B] group by CustNo,[Name],BrandName,Qty
            ) x
            pivot 
            (
                max(Qty)
                for brandname in (' + @cols + ')
            ) p '

execute(@query)

【问题讨论】:

你得到什么错误? 问题是什么?您已经编写了一些代码,但您对它不满意,所以请描述一下出了什么问题。除非您需要取决于不同类别的可变数量的列,否则您可以轻松地使用静态数据透视表并枚举数据透视表的 in 部分中的列。 【参考方案1】:

您的查询中几乎没有错误。首先,您选择了不在您的表中的列“Slab”(可能是由于从另一个查询中复制)相反,您需要选择 custno 和名称。

然后您的查询将运行,但您将为每个客户提供三行,因为每个客户在数量字段中具有三个不同的值。背后的原因是内部查询中的 group by 子句(group by CustNo,[Name],BrandName,Qty)。相反,我使用窗口函数为每个客户求和(amt)。

我还使用了两组动态列名来消除结果中的空值。一个在您的代码中使用 (@cols) 进行旋转,其他列表包含 coalesce(columnname,0) 以将 null 转换为 0。

如果您使用的是 SQL Server 2017 及更高版本,那么我建议您使用 string_agg() 来连接列名,因为它更容易且性能更快。我在 Query#2 中使用过它。

架构和插入语句:

create table [Table-A](PID int, BrandName varchar(50));
insert into [Table-A] values(1  ,'Brand1');
insert into [Table-A] values(2  ,'Brand2');
insert into [Table-A] values(3  ,'Brand3');
insert into [Table-A] values(4  ,'Brand4');
insert into [Table-A] values(5  ,'Brand5');
insert into [Table-A] values(6  ,'Brand6');
insert into [Table-A] values(7  ,'Brand7');
insert into [Table-A] values(8  ,'Brand8');

create table [TABLE-B]( CustNo  int,Name varchar(10),BrandName varchar(50),Qty int, Amt int);
insert into [TABLE-B] values(1  ,'C1',  'Brand1',   3,  300);
insert into [TABLE-B] values(1  ,'C1',  'Brand2',   2,  400);
insert into [TABLE-B] values(1  ,'C1',  'Brand4',   1,  300);
insert into [TABLE-B] values(1  ,'C1',  'Brand5',   2,  100);
insert into [TABLE-B] values(2  ,'C2',  'Brand1',   2,  200);
insert into [TABLE-B] values(2  ,'C2',  'Brand3',   1,  200);
insert into [TABLE-B] values(3  ,'C3',  'Brand2',   1,  300);
insert into [TABLE-B] values(3  ,'C3',  'Brand7',   3,  150);

查询#1(使用 stuff() 和 xml 路径 for())

DECLARE @cols AS NVARCHAR(MAX),
    @colsForSelect AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX)
 
 
SET @colsForSelect = STUFF((SELECT  ',' + ' Coalesce('+quotename(BrandName)+',0) '+ quotename(BrandName)
            FROM [TABLE-A] order by pid
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')
        
select @cols = STUFF((SELECT ',' + QUOTENAME(BrandName) from [TABLE-A] order by PID FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)') ,1,1,'')

set @query = 'SELECT custno,name,' + @colsForSelect + ',Amt
             from 
             (
select CustNo,[Name],Qty,SUM(cast([amt] as float))over(partition by custno) as Amt,BrandName from [TABLE-B]             ) x
            pivot 
            (
                max(Qty)
                for brandname in (' + @cols + ')
            ) p '

execute(@query)

输出:

custno name Brand1 Brand2 Brand3 Brand4 Brand5 Brand6 Brand7 Brand8 Amt
1 C1 3 2 0 1 2 0 0 0 1100
2 C2 2 0 1 0 0 0 0 0 400
3 C3 0 1 0 0 0 0 3 0 450

查询#2(使用 string_agg() 代替 stuff() 并用于 xml path())

DECLARE @cols AS NVARCHAR(MAX),
    @colsForSelect AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX)


select @colsForSelect= string_agg('coalesce('+BrandName+',0) '+QUOTENAME(BrandName)+' ','  ,') from [TABLE-A]

select @cols = string_agg(QUOTENAME(BrandName),',') from [TABLE-A]


set @query = 'SELECT custno,name,' + @colsForSelect + ',Amt
             from 
             (
select CustNo,[Name],Qty,SUM(cast([amt] as float))over(partition by custno) as Amt,BrandName from [TABLE-B]             ) x
            pivot 
            (
                max(Qty)
                for brandname in (' + @cols + ')
            ) p'

execute(@query)

输出:

custno name Brand1 Brand2 Brand3 Brand4 Brand5 Brand6 Brand7 Brand8 Amt
1 C1 3 2 0 1 2 0 0 0 1100
2 C2 2 0 1 0 0 0 0 0 400
3 C3 0 1 0 0 0 0 3 0 450

db小提琴here

【讨论】:

尽管这解决了问题,但它不太可能帮助除 OP 之外的其他人(因为需要读者用他/她的眼睛解析代码)。请澄清问题:出了什么问题,应该做什么以及为什么。 我没有。我刚刚从 dbfiddle.uk 复制了 markdown 文本。 @astentx 我试图解释一下。 @DaleK 我现在会尝试做那个表格。谢谢大佬。

以上是关于在 SQL Server 中使用逐行总和进行透视[关闭]的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server 2005 - 在没有总和/计数和动态值列表的情况下透视数据

SQL Server 数据透视 + 总和 + 分组依据

是否可以使用 SQL Server 使用相同的数据透视列进行多个数据透视

使用汇总(或行总计)进行 SQL 透视

为啥我们在 SQL Server 中透视文本列时使用 Max 函数?

SQL Server 转置数据 - 可能进行数据透视?