美文网首页
PIVOT、UNPIVOT 转换行与列

PIVOT、UNPIVOT 转换行与列

作者: haidaozheng | 来源:发表于2019-04-26 15:12 被阅读0次

可以使用 PIVOTUNPIVOT 运算符将表值表达式更改为另一个表。

  • PIVOT 是将表达式某一列中的唯一值转换为输出中的多个列,并在必要时对最终输出中所需的其余任何列值运行聚合。
  • PIVOT 执行的操作相反,UNPIVOT 将表值表达式的列轮换为列值。

语法

SELECT <non-pivoted column>,  
    [first pivoted column] AS <column name>,  
    [second pivoted column] AS <column name>,  
    ...  
    [last pivoted column] AS <column name>  
FROM  
    (<SELECT query that produces the data>)   AS <alias for the source query>  
PIVOT  
(  
    <aggregation function>(<column being aggregated>)  
FOR   
[<column that contains the values that will become column headers>]   
    IN ( [first pivoted column], [second pivoted column],  
    ... [last pivoted column])  
) AS <alias for the pivot table>  
<optional ORDER BY clause>;  

示例:PIVOT

基础数据准备

create table StudentScores
(
class varchar(10),               --班级
username    varchar(20),         --姓名
[subject]    varchar(30),        --科目
score        float,              --成绩
);
go
-- 01、插入测试数据
insert into StudentScores(class,username,[subject],score) values('一班','张三', '语文', 80);
insert into StudentScores(class,username,[subject],score) values('一班','张三', '数学', 90);
insert into StudentScores(class,username,[subject],score) values('一班','张三', '英语', 70);
insert into StudentScores(class,username,[subject],score) values('一班','张三', '生物', 85);
insert into StudentScores(class,username,[subject],score) values('一班','李四', '语文', 70);
insert into StudentScores(class,username,[subject],score) values('一班','李四', '数学', 92);
insert into StudentScores(class,username,[subject],score) values('一班','李四', '英语', 76);
insert into StudentScores(class,username,[subject],score) values('一班','王二', '语文', 60);
insert into StudentScores(class,username,[subject],score) values('一班','王二', '数学', 82);
insert into StudentScores(class,username,[subject],score) values('一班','王二', '英语', 96);
insert into StudentScores(class,username,[subject],score) values('一班','王二', '生物', 78);

insert into StudentScores(class,username,[subject],score) values('二班','吴六', '语文', 90);
insert into StudentScores(class,username,[subject],score) values('二班','吴六', '数学', 94);
insert into StudentScores(class,username,[subject],score) values('二班','吴六', '英语', 78);
insert into StudentScores(class,username,[subject],score) values('二班','吴六', '生物', 75);
insert into StudentScores(class,username,[subject],score) values('二班','郑五', '语文', 78);
insert into StudentScores(class,username,[subject],score) values('二班','郑五', '数学', 94);
insert into StudentScores(class,username,[subject],score) values('二班','郑五', '英语', 86);
insert into StudentScores(class,username,[subject],score) values('二班','郑五', '英语', 86);
insert into StudentScores(class,username,[subject],score) values('二班','张三', '语文', 70);
insert into StudentScores(class,username,[subject],score) values('二班','张三', '数学', 88);
insert into StudentScores(class,username,[subject],score) values('二班','张三', '英语', 90);
insert into StudentScores(class,username,[subject],score) values('二班','张三', '生物', 88);
-- 02、查询数据
select * from StudentScores;
基础数据
问题一

如果我想知道每位学生的每科成绩,而且每个学生的全部成绩排成一行

--A1、传统 case 方法
select class as '班级',username as '姓名'
,max(case [subject] when '语文' then score else 0 end) as '语文'
,max(case [subject] when '数学' then score else 0 end) as '数学'
,max(case [subject] when '英语' then score else 0 end) as '英语'
,max(case [subject] when '生物' then score else 0 end) as '生物'
from StudentScores
group by class,username
order by class desc,username desc;

-- A2:PIVOT 方法
select class as '班级',username as '姓名',[语文],[数学],[英语],[生物] 
from StudentScores a
PIVOT
(
  max(a.score) 
  for a.subject in([语文],[数学],[英语],[生物])
) b;
问题一结果
问题二

如果我想知道每个班级每门课的平均成绩(排除“姓名”聚合项)
错误示范:结果并没有在class层级聚合,依然在全部未拆分层级聚合

select class as '班级',[语文],[数学],[英语],[生物] 
from StudentScores a
PIVOT
(
    avg(a.score) 
    for a.subject in([语文],[数学],[英语],[生物])
)b;
错误示范

正确示范:首先确保数据源只保留需聚合的字段,即先在from语句中进行一次聚合

--A1、传统 case 方法
select class as '姓名'
,sum(case [subject] when '语文' then score else 0 end)/sum(case [subject] when '语文' then 1 else 0 end) as '语文'
,sum(case [subject] when '数学' then score else 0 end)/sum(case [subject] when '数学' then 1 else 0 end) as '数学'
,sum(case [subject] when '英语' then score else 0 end)/sum(case [subject] when '英语' then 1 else 0 end) as '英语'
,sum(case [subject] when '生物' then score else 0 end)/sum(case [subject] when '生物' then 1 else 0 end) as '生物'
from StudentScores
group by class;

-- A2:PIVOT 方法
select class,[语文],[数学],[英语],[生物] 
from (select class,subject,avg(score) as score 
        from StudentScores 
        group by class,subject) a
PIVOT
(
    max(a.score) 
    for a.subject in([语文],[数学],[英语],[生物])
)b;
正确示范

示例:UNPIVOT

--插入数据
select class,[语文],[数学],[英语],[生物] 
into #1
from (select class,subject,avg(score) as score from StudentScores group by class,subject) a
PIVOT
(
    max(a.score) 
    for a.subject in([语文],[数学],[英语],[生物])
) b 

select * from #1
基础数据

课程拆分到列:

select b.class,b.subject,b.score 
from #1 a
UNPIVOT
(
    score for subject in([语文],[数学],[英语],[生物])
)b;
查询结果

请注意,UNPIVOT 并不完全是 PIVOT 的逆操作。

  • PIVOT 执行聚合,并将多个可能的行合并为输出中的一行。
  • UNPIVOT 不重现原始表值表达式的结果,因为行已被合并。
    另外,UNPIVOT 输入中的 NULL 值也在输出中消失了。 如果值消失,表明在执行 PIVOT 操作前,输入中可能就已存在原始 NULL 值。

【参考】
MS:FROM - 使用 PIVOT 和 UNPIVOT
SQL Server:使用 PIVOT 行转列和 UNPIVOT 列转行

相关文章

网友评论

      本文标题:PIVOT、UNPIVOT 转换行与列

      本文链接:https://www.haomeiwen.com/subject/avdfnqtx.html