MySQL 行转列,列转行
目录
警告
本文最后更新于 2021-06-08,文中内容可能已过时。
行转列
即将原本同一列下多行的不同内容作为多个字段,输出对应内容
测试数据
建表:
|
|
插入数据:
|
|
查询表中数据:
|
|
ID | USER_ID | SUBJECT | SCORE |
---|---|---|---|
1 | 1001 | 语文 | 90 |
2 | 1001 | 数学 | 92 |
3 | 1001 | 英语 | 80 |
4 | 1002 | 语文 | 88 |
5 | 1002 | 数学 | 90 |
6 | 1002 | 英语 | 75 |
7 | 1003 | 语文 | 70 |
8 | 1003 | 数学 | 85 |
9 | 1003 | 英语 | 90 |
10 | 1003 | 政治 | 82 |
1. 使用case...when...then
|
|
2. 使用IF()
|
|
两者运行结果都为:
USER_ID | 语文 | 数学 | 英语 | 政治 |
---|---|---|---|---|
1001 | 90 | 92 | 80 | 0 |
1002 | 88 | 90 | 75 | 0 |
1003 | 70 | 85 | 90 | 82 |
可以看出,这里行转列是将原来的SUBJECT
字段的多行内容选出来,作为结果集中的不同列,并根据USER_ID
进行分组显示对应的SCORE
。
注意
SUM()
是为了能够使用GROUP BY
根据USER_ID
进行分组,因为每一个USER_ID
对应的SUBJECT=语文
的记录只有一条,所以SUM()
的值就等于对应那一条记录的score的值。- 假如
USER_ID = '1001' AND SUBJECT = '语文'
的记录有两条,则此时SUM()
的值将会是这两条记录的和,同理,使用MAX()
的值将会是这两条记录里面最大的一个。但是正常情况下,一个USER
对应一个SUBJECT
只有一个分数,因此使用 SUM()、MAX()、MIN()、AVG()等聚合函数都可以达到行转列的效果 IF('SUBJECT' = '语文', score, 0)
作为条件,即对所有SUBJECT='语文'
的记录的SCORE
字段进行SUM(), MAX(), MIN(), AVG()
操作,如果SCORE
没有值则默认为0
。
3. 利用SUM(IF())
生成列 + WITH ROLLUP
生成汇总行,并利用IFNULL
将汇总行标题显示为Total
|
|
运行结果:
USER_ID | 语文 | 数学 | 英语 | 政治 | total |
---|---|---|---|---|---|
1001 | 90 | 92 | 80 | 0 | 262 |
1002 | 88 | 90 | 75 | 0 | 253 |
1003 | 70 | 85 | 90 | 82 | 327 |
total | 248 | 267 | 245 | 82 | 842 |
4. 利用SUM(IF())
生成列 + UNION
生成汇总行,并利用IFNULL
将汇总行标题显示为TOTAL
|
|
运行结果:
USER_ID | 语文 | 数学 | 英语 | 政治 | TOTAL |
---|---|---|---|---|---|
1001 | 90 | 92 | 80 | 0 | 262 |
1002 | 88 | 90 | 75 | 0 | 253 |
1003 | 70 | 85 | 90 | 82 | 327 |
TOTAL | 248 | 267 | 245 | 82 | 842 |
5. 利用SUM(IF())
生成列,直接生成结果,不再利用子查询
|
|
运行结果:
USER_ID | 语文 | 数学 | 英语 | 政治 | TOTAL |
---|---|---|---|---|---|
1001 | 90 | 92 | 80 | 0 | 262 |
1002 | 88 | 90 | 75 | 0 | 253 |
1003 | 70 | 85 | 90 | 82 | 327 |
TOTAL | 248 | 267 | 245 | 82 | 842 |
6. 合并字段显示:利用GROUP_CONCAT()
|
|
运行结果:
USER_ID | 成绩 |
---|---|
1001 | 语文:90,数学:92,英语:80 |
1002 | 语文:88,数学:90,英语:75 |
1003 | 语文:70,数学:85,英语:90,政治:82 |
GROUP_CONCAT()
,手册上说明:该函数返回带有来自一个组的连接的非NULL
值的字符串结果。
比较抽象,难以理解。通俗点理解,其实是这样的:GROUP_CONCAT()
会计算哪些行属于同一组,将属于同一组的列显示出来。要返回哪些列,由函数参数(就是字段名)决定。分组必须有个标准,就是根据GROUP BY
指定的列进行分组。
列转行
测试数据
建表:
|
|
插入数据:
|
|
查询表中数据:
|
|
ID | USER_ID | CN_SCORE | MATH_SCORE | EN_SCORE | PO_SCORE |
---|---|---|---|---|---|
1 | 1001 | 90 | 92 | 80 | 0 |
2 | 1002 | 88 | 90 | 75.5 | 0 |
3 | 1003 | 70 | 85 | 90 | 82 |
UNION ALL
本质是将 USER_ID 的每个科目分数分散成一条记录显示出来。
|
|
运行结果:
USER_ID | COURSE | SCORE |
---|---|---|
1001 | 语文 | 90 |
1001 | 数学 | 92 |
1001 | 英语 | 80 |
1001 | 政治 | 0 |
1002 | 语文 | 88 |
1002 | 数学 | 90 |
1002 | 英语 | 75.5 |
1002 | 政治 | 0 |
1003 | 语文 | 70 |
1003 | 数学 | 85 |
1003 | 英语 | 90 |
1003 | 政治 | 82 |
注意
附:UNION 与 UNION ALL 的区别:
- 对重复结果的处理:UNION 会去掉重复记录,UNION ALL 不会;
- 对排序的处理:UNION 会排序,UNION ALL 只是简单的将两个结果集合合并;
- 效率方面的区别:因为 UNION 会做去重和排序,因此效率比 UNION ALL 慢很多。