在 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 使用相同的数据透视列进行多个数据透视