Write a SQL query to convert table from A to B

百般思念 提交于 2020-07-10 10:26:47

问题


I have a database table (table A) looks like this, in SQL Server 2016:

Table A:

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

I want to write a SQL query or something like that or a reporting tool to generate a report/table (convert table A to table B) as below:

Table 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

If it can be achieved by writing a SQL query that would be great. If that it needs to use a programming language to write a program, or use a tool, that will also be OK but please let me know what to use and how to achieve (I know some C# programming).

(I know I should not use ID and Group as the column name. I will not really use that name in the database table, just to simplify the question here)

Anyone can help? Thank you very much!


回答1:


A canonical method uses 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;

However, in SQL Server, I strongly recommend 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;



回答2:


Just in case you have variable or additional columns over time.

Notice you just have to EXCLUDE certain columns and NULLS are omitted.

Full Disclosure: Gordon's APPLY is a nudge more performant.

Example

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

Returns

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

EDIT -- XML Version if NOT 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



回答3:


Here is my solution, to exactly match your need

I surrounded the keywords and year as column names with square brackets

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]

Here is the fiddle




回答4:


solution using UNPIVOT:

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

Hope it helps.!



来源:https://stackoverflow.com/questions/62648585/write-a-sql-query-to-convert-table-from-a-to-b

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!