如何将列数据转换为行数据 sql?

How can I convert columns data into row data sql?

我有一个像这样的table,它像这样存储classes。大概有 30 classes 以这种方式列出。 class 名称的一些示例是 'Blood Borne Safety'、'CPR Training'、'Fork Lift'。

Name  |    Class1     |  Class1TrainerName  |  Class2   | Class2TrainerName 
------|---------------|---------------------|-----------|---------------
Scott |    3/28/2017  |  Casey              | 4/19/2017 |  David
Jim   |               |                     | 2/9/2019  |  David

我想把它变成这样。

Name    |   ClassName  |  ClassDate  |  Trainer | 
--------|--------------|-------------|----------|
Scott   |   Class1     |  3/28/2017  | Casey    |
Scott   |   Class2     |  4/19/2017  | David    |
Jim     |   Class2     |  2/9/2019   | David    |

如何将 table1 转换为 table2 的数据?

这个方法在this article.

上有详细解释

它利用 table 值构造函数的优势从单个行生成多行。它还使用 APPLY 运算符在单次读取中完成。请注意,第一列可以包含将成为您的 Class 姓名的任何字符串。其他 2 列是未旋转的列。

SELECT mt.[Name],
    up.ClassName, 
    up.ClassDate, 
    up.Trainer
FROM MyTable mt
CROSS APPLY (VALUES('Class1', Class1, Class1TrainerName),
                   ('Class2', Class2, Class2TrainerName),
                   ('Class3', Class3, Class3TrainerName)) AS up(ClassName, ClassDate, Trainer);

只是另一个选项,它将 "dynamically" 在不实际使用动态 SQL

的情况下对数据进行反透视

当然,Luis 的解决方案会更高效,但在这里您不必详细说明所有列名和数据类型。

例子

Declare @YourTable Table ([Name] varchar(50),[Class1] date,[Class1TrainerName] varchar(50),[Class2] date,[Class2TrainerName] varchar(50))
Insert Into @YourTable Values 
 ('Scott','3/28/2017','Casey','4/19/2017','David')
,('Jim','2/9/2019','David',null,null)


;with cte as (
    Select RN
          ,A.Name
          ,C.*
          ,Grp = sum(case when Item not like '%TrainerName' then 1 end) over (Partition by RN Order by Seq)
     From  (Select *,RN = Row_Number() over (Order by (Select null)) From @YourTable) A
     Cross Apply ( values (cast((Select A.* for XML RAW) as xml))) B(XMLData)
     Cross Apply (
                    Select Item  = xAttr.value('local-name(.)', 'varchar(100)')
                          ,Value = xAttr.value('.','varchar(max)')
                          ,Seq   = Row_Number() over (Order by (Select null))
                     From  XMLData.nodes('//@*') xNode(xAttr)
                     Where xAttr.value('local-name(.)','varchar(100)') not in ('Name','RN')
                 ) C
) 
Select Name
      ,ClassName  = max(case when Item not like '%TrainerName' then Item end)
      ,ClassDate  = max(case when Item not like '%TrainerName' then Value end)
      ,Trainner   = max(case when Item like     '%TrainerName' then Value end)
 From cte
 Group By RN,Name,Grp

Returns

Name    ClassName   ClassDate   Trainner
Scott   Class1      2017-03-28  Casey
Scott   Class2      2017-04-19  David
Jim     Class1      2019-02-09  David