我有下表:
+----------------------------------------------------------------------+
| Student Class1 Class1_score Class2 Class2_score Class3.... |
+----------------------------------------------------------------------+
| Alex English 70 Maths 100 NULL |
| John Science 50 NULL NULL NULL |
| Bruce Maths 50 Science 50 English |
+----------------------------------------------------------------------+
我希望转向以下内容:
+--------------------------+
| Student Class Score |
+--------------------------+
| Alex English 70 |
| Alex Maths 100 |
| Alex Science NULL |
| Alex History NULL |
| John English NULL |
+--------------------------+
其中包含不属于该特定学生原始表格的类的NULL(例如Science for Alex)
如何在SQL中实现这一目标?
答案 0 :(得分:1)
假设类类型只在classX
列中出现一次:
select student,
'English' class,
coaslesce(case when class1 = 'english' then Class1_score end,
case when class2 = 'english' then Class2_score end,
case when class3 = 'english' then Class3_score end,
case when class4 = 'english' then Class4_score end) score
from your_table
union
select student,
'Maths' class,
coaslesce(case when class1 = 'Maths' then Class1_score end,
case when class2 = 'Maths' then Class2_score end,
case when class3 = 'Maths' then Class3_score end,
case when class4 = 'Maths' then Class4_score end) score
from your_table
union
select student,
'Science' class,
coaslesce(case when class1 = 'Science' then Class1_score end,
case when class2 = 'Science' then Class2_score end,
case when class3 = 'Science' then Class3_score end,
case when class4 = 'Science' then Class4_score end) score
from your_table
union
select student,
'History' class,
coaslesce(case when class1 = 'History' then Class1_score end,
case when class2 = 'History' then Class2_score end,
case when class3 = 'History' then Class3_score end,
case when class4 = 'History' then Class4_score end) score
from your_table
答案 1 :(得分:1)
如果您希望每个Class
列值都包含所有Student
列值,则声明2个表变量,一个用于存储Student
列值的唯一值,另一个用于存储Class
列值的唯一值。然后在这两个表之间进行完全外连接,并将此结果集用作子集,并将其与另一个表变量连接,该变量包含每个student
class
和score
的单独行。所有这些都可以通过执行动态SQL查询来完成。
<强>查询强>
-- table variabe to store unique class values
declare @sql as varchar(max) = 'declare @tbl_class as table([class] varchar(100));'
+ 'insert into @tbl_class([class]) ';
select @sql += stuff((
select ' union select [' + [column_name] + '] from [' + [table_name] + '] '
+ 'where [' + [column_name] + '] is not null'
from information_schema.columns
where [table_name] = 'your_table_name'
and [column_name] like 'class%[0-9]'
order by [ordinal_position]
for xml path('')
)
, 1, 7, ''
) + ';';
-- table variabe to store unique student values
select @sql += 'declare @tbl_student as table([student] varchar(100));'
+ 'insert into @tbl_student([student]) '
+ 'select distinct [Student] from [your_table_name];';
-- table variable to store student score and class values one by one
select @sql += 'declare @tbl_scores as table'
+ '([student] varchar(100), [class] varchar(100), [score] int);'
+ 'insert into @tbl_scores([student], [class], [score]) ';
select @sql += stuff((
select ' union all select [Student], '
+ '[' + t.[col_1] + '] as [class],'
+ '[' + t.[col_2] + '] as [score] '
+ 'from [' + t.[table_name] + '] '
from (
select [column_name] as [col_1], [column_name] + '_score' as [col_2],
[ordinal_position], [table_name]
from information_schema.columns
where [table_name] = 'your_table_name'
and [column_name] like 'class%[0-9]'
) t
order by t.[ordinal_position]
for xml path('')
)
, 1, 10, ''
) + ';';
-- final result
select @sql += 'select t.[student], t.[Class], tsc.[Score] from('
+ 'select ts.[student], tc.[Class]'
+ ' from @tbl_student as ts '
+ ' full outer join @tbl_class as tc '
+ 'on 1 = 1 ) t '
+ 'left join @tbl_scores as tsc '
+ 'on t.[student] = tsc.[student] '
+ 'and t.[Class] = tsc.[Class];';
exec(@sql);
<强> Find a demo here 强>
答案 2 :(得分:0)
你实际上是在寻找unpivot(从宽到高),而不是透视。一种应该适用于几乎所有数据库的方法都使用一系列联合:
SELECT Student, Class, Score
FROM
(
SELECT Student, Class1 AS Class, Class1_score AS Score, 1 AS position
FROM yourTable
UNION ALL
SELECT Student, Class2, Class2_score, 2
FROM yourTable
UNION ALL
SELECT Student, Class3, Class3_score, 3
FROM yourTable
) t
ORDER BY
Student, position;
答案 3 :(得分:0)
示例数据
IF OBJECT_ID('dbo.tempdata') IS NOT NULL
DROP TABLE tempdata
;With cte(Student , Class1 , Class1_score, Class2, Class2_score, Class3,Class3_score)
AS
(
SELECT 'Alex' ,'English', 70,'Maths' , 100 , NULL ,NULL UNION ALL
SELECT 'John' ,'Science', 50,NULL ,NULL , NULL ,NULL UNION ALL
SELECT 'Bruce','Maths' , 50,'Science' , 50 , 'English',NULL
)
SELECT * INTO tempdata FROM cte
SELECT * FROM tempdata
使用交叉应用和动态SQL
DECLARE @DynamicColumns NVARCHAR(max)
,@Sql NVARCHAR(max)
;WITH cte
AS
(
SELECT COLUMN_NAME,
DENSE_RANK()OVER(ORDER BY SUBSTRING(COLUMN_NAME,0,7)) As BatchNo
FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='tempdata' AND COLUMN_NAME <>'Student'
)
SELECT @DynamicColumns=STUFF((SELECT ', '+ReqCol FROM
(
SELECT
DISTINCT '('+STUFF((SELECT ', '+COLUMN_NAME
FROM CTE i WHERE i.BatchNo=o.BatchNo
FOR XML PATH ('')),1,1,'') + ')' AS ReqCol
FROM cte o
)dt
FOR XML PATH ('')),1,1,'')
SET @Sql='SELECT Student
,Class
,Score
FROM tempdata
CROSS APPLY (VALUES '+@DynamicColumns+'
) As Dt (Class, Score)
WHERE Class IS NOT NULL
'
PRINT @Sql
EXEC (@Sql)
结果
Student Class Score
---------------------
Alex English 70
Alex Maths 100
John Science 50
Bruce Maths 50
Bruce Science 50
Bruce English NULL