【问题标题】:How can I convert columns data into row data sql?如何将列数据转换为行数据 sql?
【发布时间】:2019-04-15 20:26:28
【问题描述】:

我有一个这样的表,它存储这样的类。以这种方式列出的可能有 30 个类。类名称的一些示例包括“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 的数据?

【问题讨论】:

  • SO 不是代码编写服务,您需要向我们展示您的尝试。
  • 你可以看看 UNPIVOT
  • Class3、Class4、..等列还有吗?
  • 然后编辑您的问题以提及这一点。
  • 我只是希望你的任务是修复这个桌子设计。那是丑陋的。这里增加的复杂性是您甚至不能使用标准的 UNPIVOT,因为对于某些列,列名是数据,而在其他列中,它仍然是列名。

标签: sql sql-server data-conversion


【解决方案1】:

这是this article上详细解释的方法。

它利用表值构造函数的优势从单行生成多行。它还使用 APPLY 运算符在单次读取中执行此操作。请注意,第一列可以包含将成为您的类名的任何字符串。其他 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);

【讨论】:

  • 非常感谢您的回答!这会很好用。
  • 我唯一要添加的是这样的 where 子句where up.ClassDate is not null AND up.ClassDate <> ''
【解决方案2】:

只是另一个选项,可以“动态地”在不实际使用动态 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

退货

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

【讨论】:

  • 惊人的答案。这很有帮助,而且更容易。我知道更好理解它
  • 另外,如果有人在看这个并且在他们的列标题中有很多特殊字符(对我来说是“ClassName”),那么你需要用Select Item = Replace(Replace(Replace(Replace(Replace(Replace(Replace(xAttr.value('local-name(.)', 'varchar(200)'), '_x0020_', ' '), '_x0028_', '('), '_x0029_', ')'), '_x002C_', ','), '_x0026_', '&'), '_x0027_', ''''), '_x002F_', '/')替换Select Item = xAttr.value('local-name(.)', 'varchar(100)')跨度>
  • @thalacker 啊,你有特殊字符......可能有其他选择。给我一点实验。
  • 是的,我知道,也许我应该提到这一点。但是,一旦我像上面提到的那样进行了替换,一切都解决了!此外,我必须运行一个快速程序,以确保我的所有空白值都是 'null' 而不是 ' '。
  • @thalacker 那么干得好!关于空白值......也许最终选择中的一个简单 WHERE 可能会有所帮助。 ... WHERE C.Value'' 弄清楚事情总是很有趣:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多