动态SQL查询使用unpivot将未知数量的列转置为行

时间:2018-09-03 05:18:10

标签: sql sql-server tsql unpivot

我有一个表,其中包含未知数量的任务,像这样:

+----------+--------+--------+--------+--------+--------+
| CourseID | Task 1 | Task 2 | Task 3 | Task 4 | Task 5 |
+----------+--------+--------+--------+--------+--------+
| EN01     |     15 |     20 |     15 |     25 |     30 |
+----------+--------+--------+--------+--------+--------+

有时有5个任务,有时还有更多任务。我如何使用数据透视表编写动态转置查询以获取此结果,而不必专门修饰除列标题之外的其他列标题,例如“ Task%”

+----------+-------------+------------+
| CourseID | Task Number | Task Total |
+----------+-------------+------------+
| EN01     | Task 1      |         15 |
| EN01     | Task 2      |         20 |
| EN01     | Task 3      |         15 |
| EN01     | Task 4      |         25 |
| EN01     | Task 5      |         30 |
+----------+-------------+------------+

编辑:基本上,我需要相反的操作:Efficiently convert rows to columns in sql server

我可以手动完成:

-- Unpivot the table.
 SELECT [Class], TaskNumber, TaskTotal
FROM
   (SELECT [Class], [Task 1], [Task 2], [Task 3], [Task 4]
   FROM [Modules].[dbo].[2018-12ah] where [Given] like '%J:%') p
UNPIVOT
   (TaskTotal FOR TaskNumber IN
      ([Task 1], [Task 2], [Task 3], [Task 4])
)AS Unpivot;
GO

下一步,我不确定该如何在任务1到Z中动态构建,因此不必在查询中指定它们。

谢谢

1 个答案:

答案 0 :(得分:0)

您可以将INFORMATION_SCHEMA中的信息用于动态TSQL,但要注意安全隐患(即SQL注入):

create table  [dbo].[Test]  ([CourseID] nvarchar(50), [Task 1] int, [Task 2] int, [Task 3] int, [Task 4] int, [Task 5] int) 

insert into [dbo].[Test] select 'EN01', 15,20,15,25,30

declare @sql nvarchar(max) = ''
declare @cols nvarchar(max) = ''

select @cols = @cols +','+ QUOTENAME(COLUMN_NAME)
from INFORMATION_SCHEMA.COLUMNS
where TABLE_SCHEMA='dbo' and TABLE_NAME='test' and COLUMN_NAME like 'Task%' 
order by ORDINAL_POSITION

set @cols = substring(@cols, 2, LEN(@cols))

set @sql = @sql + ' select u.[CourseID], u.[TaskNumber], u.[TaskTotal] '
set @sql = @sql + ' from [dbo].[Test] s '
set @sql = @sql + ' unpivot '
set @sql = @sql + ' ( '
set @sql = @sql + '     [TaskTotal] '
set @sql = @sql + '     for [TaskNumber] in ('+ @cols + ') '
set @sql = @sql + ' ) u;'

execute(@sql)

包含5个任务的结果:

enter image description here

如果更改表结构,请添加新的任务列:

DROP TABLE [dbo].[Test]
create table  [dbo].[Test]  ([CourseID] nvarchar(50), [Task 1] int, [Task 2] int, [Task 3] int, [Task 4] int, [Task 5] int, [Task 6] int) 
insert into [dbo].[Test] select 'EN01', 15,20,15,25,30,1000

同一查询将返回此结果集:

enter image description here

如果您更喜欢使用CROSS APPLY方法来取消透视,这里是替代解决方案:

create table  [dbo].[Test]  ([CourseID] nvarchar(50), [Task 1] int, [Task 2] int, [Task 3] int, [Task 4] int, [Task 5] int) 

insert into [dbo].[Test] select 'EN01', 15,20,15,25,30 

declare @sql nvarchar(max) = ''
declare @values nvarchar(max) = ''

select @values = @values +',(''' + COLUMN_NAME + ''','+ QUOTENAME(COLUMN_NAME) + ')'
from INFORMATION_SCHEMA.COLUMNS
where TABLE_SCHEMA='dbo' and TABLE_NAME='test' and COLUMN_NAME like 'Task%' 
order by ORDINAL_POSITION

set @values = substring(@values, 2, LEN(@values))

set @sql = @sql + ' Select [CourseID], B.* '
set @sql = @sql + ' From [dbo].[Test] '
set @sql = @sql + ' Cross Apply (values '
set @sql = @sql + @values
set @sql = @sql + ' ) B(TaskNumber, TaskTotal) '

execute(@sql)