SQL 将列转换为行
Posted
技术标签:
【中文标题】SQL 将列转换为行【英文标题】:SQL Converting Columns to Rows 【发布时间】:2020-06-30 18:56:41 【问题描述】:我要执行以下转换:
Seniority Price Rating
---------------------------
1 P1 R1
2 P2 R2
3 P3 R3
到
Value RowId ColId
------------------
1 0 0
P1 0 1
R1 0 2
2 1 0
P2 1 1
R2 1 2
3 2 0
P3 2 1
R3 2 2
如果可能的话,我希望在转换后的表中也保留字段名称,即所有 rowid=0 的行都将添加 Seniority 作为字段,rowid=1 将具有价格,依此类推。
【问题讨论】:
用您正在使用的数据库标记您的问题。 【参考方案1】:这是一个使用 JSON 的选项,如果 2016+ XML 版本可用于旧版本。
示例
Declare @YourTable Table ([Seniority] varchar(50),[Price] varchar(50),[Rating] varchar(50))
Insert Into @YourTable Values
(1,'P1','R1')
,(2,'P2','R2')
,(3,'P3','R3')
Select B.Value
,A.RowID
,B.ColID
From (Select *
,RowID = row_number() over (order by (select null)) - 1
From @YourTable --<<< Replace with virtually any table.
) A
Cross Apply (
Select *
,ColID = row_number() over (order by (select null)) - 1
From OpenJson( (Select A.* For JSON Path,Without_Array_Wrapper,INCLUDE_NULL_VALUES ) )
Where [Key] not in ('RowID')
) B
退货
编辑 - 只是为了好玩,XML 版本
Select C.Value
,A.RowID
,C.ColID
From (Select *
,RowID = row_number() over (order by (select null)) - 1
From @YourTable
) A
Cross Apply ( values ( convert(xml,(Select A.* for XML RAW)) ) ) B(XMLData)
Cross Apply (
Select ColID = row_number() over( order by (select null)) - 1
,Value = xAttr.value('.','varchar(max)')
From XMLData.nodes('//@*') xNode(xAttr)
Where xAttr.value('local-name(.)', 'varchar(100)') not in ('RowID')
) C
编辑 -- XML 版本允许 NULL 值
Declare @YourTable Table ([Seniority] varchar(50),[Price] varchar(50),[Rating] varchar(50))
Insert Into @YourTable Values
(1,'P1','R1')
,(2,'P2','R2')
,(3,NULL,'R3') -- Forced a NULL value
Select C.Value
,A.RowID
,C.ColID
From (Select *
,RowID = row_number() over (order by (select null)) - 1
From @YourTable
) A
Cross Apply ( values ( convert(xml,(Select A.* for XML RAW,ELEMENTS XSINIL)) ) ) B(XMLData)
Cross Apply (
Select Item = attr.value('local-name(.)','varchar(100)')
,Value = attr.value('.','varchar(max)')
,ColID = row_number() over (order by (select null)) - 1
From XMLData.nodes('/row') as C1(nd)
Cross Apply C1.nd.nodes('./*') as C2(attr)
Where attr.value('local-name(.)','varchar(100)') not in ('RowID')
) C
退货
Value RowID ColID
1 0 0
P1 0 1
R1 0 2
0 0 3
2 1 0
P2 1 1
R2 1 2
1 1 3
3 2 0
2 1 --<< Notice NULLs return as Empty Strings
R3 2 2
2 2 3
【讨论】:
我也想包括 NULL,如果你不知道的话。否则我自己实现。 @MarkC NULLS 从 JSON 版本中排除。但是,在 XML 版本中,有一个调整会将 NULL 作为空字符串(不是 null)传递 我使用的是 XML 版本。这种调整会很好用。 @MarkC Forgot the Where Item not in ('RowID') --- 已更新 我不在 2016 年以上。感谢您的广泛帮助。【参考方案2】:作为一般方法,union all
有效:
select seniority, 'seniority' as which, seniority - 1, 0
from t
union all
select p1, 'p1' as which, seniority - 1, 1
from t
union all
select r1, 'r1' as which, seniority - 1, 2
from t;
【讨论】:
我想看看是否有人使用 UNPIVOT 提供了良好的动态解决方案。我需要将几乎所有给定的表格转换为该格式。我不确定这是否是一种好方法,因为我必须使用动态 sql 来生成这一切。以上是关于SQL 将列转换为行的主要内容,如果未能解决你的问题,请参考以下文章