问题
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