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 将列转换为行的主要内容,如果未能解决你的问题,请参考以下文章

SQL 查询将列转换为行

SQL Server:将列转换为行

需要 PL/SQL 存储过程将列转换为行

使用pivot SQL将列转换为行

SQL:在 Chartio 中动态地将列转换为行

SQL Server 2008 R2,将列转换为行,将行转换为列[重复]