SQL 将列转换为行

SQL Converting Columns to Rows

我要执行以下转换:

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

如果可能,我想在转换后的 table 中也保留字段名称,即所有 rowid=0 的行都将添加 Seniority 作为字段,rowid=1 将添加 Price,依此类推.

作为一般方法,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;

如果 2016+ 有一个 XML 版本可用于旧版本,这里是一个使用 JSON 的选项。

例子

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

 

Returns

编辑 - 仅供娱乐,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允许空值的版本

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

Returns

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