一年没有写过复杂SQL,今天偶然在群里看到一条面试题:将一张课程成绩表里的成绩按照用户名进行汇总。具体汇总还是用图更一目了然:
于是屁颠屁颠跑去建表准备数据挑战一下,sql如下:
create table t_user_score(
id int unsigned primary key auto_increment,
user_name varchar(16),
course_name varchar(32),
score double
);
insert into
t_user_score(user_name, course_name, score)
VALUES
('张三', '数学', 34),
('张三', '语文', 58),
('张三', '英语', 58),
('李四', '数学', 45),
('李四', '语文', 87),
('李四', '英语', 45),
('王五', '数学', 76),
('王五', '语文', 34),
('王五', '英语', 89)
;
看到这个需求,心里想:“这东西就不能直接查出来然后再业务代码中进行统计吗,毕竟是面试题,也没办法。”
思路: 这种凭空多出几列的好像 join
能做,因此写出了以下代码:
select m.user_name, m.score, m1.score, m2.score
from t_user_score as m
left join t_user_score as m1 on m.user_name = m1.user_name
left join t_user_score as m2 on m.user_name = m2.user_name
where m.course_name = '数学'
and m1.course_name = '语文'
and m2.course_name = '英语'
选用 left join
原因是:
运行一番的确可以达到行转列的目的。然后偷偷去搜索一下其他人是怎样实现行转列的,结果看到的大多数使用case when
实现的,实现代码如下:
select t.user_name, max(math) as math, max(chinese) as chinese, max(english) as english
from (
select user_name,
case
when course_name = '数学' then score
end as math,
case
when course_name = '语文' then score
end as chinese,
case
when course_name = '英语' then score
end as english
from t_user_score
) as t
group by t.user_name
;
该实现是基于Oracle的decode()
函数改编的,全表扫描是避免不了的,不要指望加什么索引进行优化了,其实我内心还是比较拒绝这道题的!