编写 SQL 查询将表从 A 转换为 B

Posted

技术标签:

【中文标题】编写 SQL 查询将表从 A 转换为 B【英文标题】:Write a SQL query to convert table from A to B 【发布时间】:2020-06-30 00:31:09 【问题描述】:

我有一个数据库表(表 A),在 SQL Server 2016 中如下所示:

表 A:

ID     Group       2018     2019    2020
-----------------------------------------
ID1    Group A      200      300     400
ID2    Group B      100      800     ---
ID2    Group B      ----     500     300

我想编写一个 SQL 查询或类似的东西或一个报告工具来生成一个报告/表(将表 A 转换为表 B),如下所示:

表 B:

ID       Group   -   Year  -      Value
----------------------------------------
ID1      Group A     2018         200
ID1      Group A     2019         300
ID1      Group A     2020         400
ID2      Group B     2018         100
ID2      Group B     2019         800
ID2      Group B     2019         500
ID2      Group B     2020         300

如果可以通过编写 SQL 查询来实现,那就太好了。如果它需要使用编程语言编写程序或使用工具,那也可以,但请告诉我使用什么以及如何实现(我知道一些 C# 编程)。

(我知道我不应该使用 ID 和 Group 作为列名。我不会在数据库表中真正使用该名称,只是为了简化这里的问题)

有人可以帮忙吗?非常感谢!

【问题讨论】:

用您正在使用的数据库标记您的问题。 【参考方案1】:

规范方法使用union all

select id, group, 2018 as year, "2018" from t
union all
select id, group, 2019 as year, "2019" from t
union all
select id, group, 2020 as year, "2018" from t;

但是,在 SQL Server 中,我强烈推荐apply

select t.id, t.group, v.*
from t cross apply
     (values (2018, [2018]), (2019, [2019]), (2020, [2020])
     ) v(year, value)
where v.value is not null;

【讨论】:

哇....非常感谢@Gordon!我现在在家,没有环境。明天我会在工作中尝试它,并让你知道它是否对我有用。非常感谢! 我试过了,效果很好!!如果 value = null,有没有办法过滤掉? 我在最后添加了条件“其中值不为空”并且有效。谢谢!【参考方案2】:

以防万一您有随时间变化的列或附加列。

请注意,您只需要 EXCLUDE 某些列并省略 NULLS。

完全披露:Gordon 的 APPLY 性能更佳。

示例

Select A.[ID]
      ,A.[Group]
      ,[Year] = B.[Key]
      ,[Value] = try_convert(int,B.[Value])  --<< choose the proper data type (optional)
 From  YourTable A
 Cross Apply (
                Select *
                 From OpenJson( (Select A.* For JSON Path,Without_Array_Wrapper ) ) 
                 Where [Key] not in ('ID','Group')
             ) B

返回

ID  Group       Year    Value
ID1 Group A     2018    200
ID1 Group A     2019    300
ID1 Group A     2020    400
ID2 Group B     2018    100
ID2 Group B     2019    800
ID2 Group B     2019    500
ID2 Group B     2020    300

编辑 -- XML 版本,如果不是 2016+

Select A.[ID]
      ,A.[Group]
      ,[Year]  = replace(replace(C.[Item],'X003',''),'_','')
      ,[Value] = try_convert(int,C.[Value])  --<< choose the proper data type (optional)
 From  YourTable A
 Cross Apply ( values ( convert(xml,(Select A.* for XML RAW)) ) ) B(XMLData)
 Cross Apply (
                Select Item  = xAttr.value('local-name(.)', 'varchar(100)')
                      ,Value = xAttr.value('.','varchar(max)')
                 From  XMLData.nodes('//@*') xNode(xAttr)
                 Where xAttr.value('local-name(.)', 'varchar(100)') not in ('ID','Group')
             ) C

【讨论】:

谢谢@John。我试过了,但不确定运行它需要什么环境或工具。它似乎在 MS SQL Server Management Studio 下不起作用,请问我应该在哪个环境或工具中运行此代码? @Denis 这在 SQL Server 2016+ 上运行 测试您的环境 只需执行 Select @@version @Denis 有一种 XML 方法,但它不喜欢以数字开头的列名。只需要一个小工作 @Denis 如果有帮助,我添加了一个 XML 版本【参考方案3】:

使用 UNPIVOT 的解决方案:

select * from A
UNPIVOT 
(
   [value] for [Year] in ([2018],[2019],[2020])
)u;

希望对你有帮助!

【讨论】:

【参考方案4】:

这是我的解决方案,完全符合您的需求

我将关键字和年份作为列名用方括号括起来

Select id, [Group], [year], value
from
(
select id, [Group], 2018 as [year], [2018] as value from a
union all
select id, [Group], 2019 as [year], [2019] as value from a
union all
select id, [Group], 2020 as [year], [2020] as value from a
) Q
Where value is not null
order by id, [Group], [year]

这里是fiddle

【讨论】:

非常感谢@Srinika。此解决方案也有效。

以上是关于编写 SQL 查询将表从 A 转换为 B的主要内容,如果未能解决你的问题,请参考以下文章

红移。我们如何(动态地)将表从列转置为行?

如何将表列转换为数组?

将表转换为像字典一样的 json 对象 SQL?

使用 T-SQL 将表转换为 XML

使用c#将表从Sql Server导出到PDF文件

ANSI sql将行动态转换为列数据