即将原本同一列下多行的不同内容作为多个字段,输出对应内容。
查询数据表中的内容(即转换前的结果)
mysql> select * from tb_score;
+----+--------+---------+-------+
| id | userid | subject | score |
+----+--------+---------+-------+
| 1 | 001 | 语文 | 90 |
| 2 | 001 | 数学 | 92 |
| 3 | 001 | 英语 | 80 |
| 4 | 002 | 语文 | 88 |
| 5 | 002 | 数学 | 90 |
| 6 | 002 | 英语 | 75.5 |
| 7 | 003 | 语文 | 70 |
| 8 | 003 | 数学 | 85 |
| 9 | 003 | 英语 | 90 |
| 10 | 003 | 政治 | 82 |
+----+--------+---------+-------+
10 rows in set (0.00 sec)
这里行转列是将原来的 subject
字段的多行内容选出来,作为结果集中的不同列,并根据userid
进行分组显示对应的 score
。
+--------+--------+--------+--------+--------+
| userid | 语文 | 数学 | 英语 | 政治 |
+--------+--------+--------+--------+--------+
| 001 | 90 | 92 | 80 | 0 |
| 002 | 88 | 90 | 75.5 | 0 |
| 003 | 70 | 85 | 90 | 82 |
+--------+--------+--------+--------+--------+
3 rows in set (0.00 sec)
SELECT userid,
SUM(CASE `subject` WHEN '语文' THEN score ELSE 0 END) as '语文',
SUM(CASE `subject` WHEN '数学' THEN score ELSE 0 END) as '数学',
SUM(CASE `subject` WHEN '英语' THEN score ELSE 0 END) as '英语',
SUM(CASE `subject` WHEN '政治' THEN score ELSE 0 END) as '政治'
FROM tb_score
GROUP BY userid;
SELECT userid,
SUM(IF(`subject`='语文',score,0)) as '语文',
SUM(IF(`subject`='数学',score,0)) as '数学',
SUM(IF(`subject`='英语',score,0)) as '英语',
SUM(IF(`subject`='政治',score,0)) as '政治'
FROM tb_score
GROUP BY userid;
注意点:
SUM()
是为了能够使用 GROUP BY
根据 userid
进行分组,因为每一个 userid
对应的subject="语文"
的记录只有一条,所以 SUM()
的值就等于对应那一条记录的 score
的值。
假如 userid ='001' and subject='语文'
的记录有两条,则此时 SUM()
的值将会是这两条记录的和,同理,使用 Max()
的值将会是这两条记录里面值最大的一个。
但是正常情况下,一个 user
对应一个 subject
只有一个分数,因此可以使用 SUM()
、MAX()、MIN()、AVG()
等聚合函数都可以达到行转列的效果。
IF(`subject`='语文',score,0) 作为条件,
即对所有 subject='语文' 的记录的 score 字段进行 SUM()、MAX()、MIN()、AVG()操作,
如果 score 没有值则默认为0。
SELECT IFNULL(userid,'total') AS userid,
SUM(IF(`subject`='语文',score,0)) AS 语文,
SUM(IF(`subject`='数学',score,0)) AS 数学,
SUM(IF(`subject`='英语',score,0)) AS 英语,
SUM(IF(`subject`='政治',score,0)) AS 政治,
SUM(IF(`subject`='total',score,0)) AS total
FROM(
SELECT userid,IFNULL(`subject`,'total') AS `subject`,SUM(score) AS score
FROM tb_score
GROUP BY userid,`subject`
WITH ROLLUP
HAVING userid IS NOT NULL
)AS A
GROUP BY userid
WITH ROLLUP;
+--------+--------+--------+--------+--------+-------+
| userid | 语文 | 数学 | 英语 | 政治 | total |
+--------+--------+--------+--------+--------+-------+
| 001 | 90 | 92 | 80 | 0 | 262 |
| 002 | 88 | 90 | 75.5 | 0 | 253.5 |
| 003 | 70 | 85 | 90 | 82 | 327 |
| total | 248 | 267 | 245.5 | 82 | 842.5 |
+--------+--------+--------+--------+--------+-------+
4 rows in set, 2 warnings (0.00 sec)
IFNULL() 函数用于判断第一个表达式是否为 NULL,如果为 NULL 则返回第二个参数的值,如果不为 NULL 则返回第一个参数的值。
实例
第一个参数为 NULL:
SELECT IFNULL(NULL, "RUNOOB");
以上实例输出结果为:RUNOOB
第一个参数不为 NULL:
SELECT IFNULL("Hello", "RUNOOB");
以上实例输出结果为:Hello
当需要对数据库数据进行分类统计的时候,往往会用上 groupby 进行分组。
而在 groupby 后面还可以加入 withcube
和 withrollup
等关键字对数据进行汇总。
SELECT
IFNULL(SUBJECT, "统计") as total,
SUM(score) AS score
FROM
tb_score
GROUP BY
SUBJECT WITH ROLLUP;
+--------+-------+
| total | score |
+--------+-------+
| 政治 | 82 |
| 数学 | 267 |
| 英语 | 245.5 |
| 语文 | 248 |
| 统计 | 842.5 |
+--------+-------+
5 rows in set (0.00 sec)
SELECT userid,
SUM(IF(`subject`='语文',score,0)) AS 语文,
SUM(IF(`subject`='数学',score,0)) AS 数学,
SUM(IF(`subject`='英语',score,0)) AS 英语,
SUM(IF(`subject`='政治',score,0)) AS 政治,
SUM(score) AS TOTAL
FROM tb_score
GROUP BY userid
UNION
SELECT 'TOTAL',SUM(IF(`subject`='语文',score,0)) AS 语文,
SUM(IF(`subject`='数学',score,0)) AS 数学,
SUM(IF(`subject`='英语',score,0)) AS 英语,
SUM(IF(`subject`='政治',score,0)) AS 政治,
SUM(score) FROM tb_score;
+--------+--------+--------+--------+--------+-------+
| userid | 语文 | 数学 | 英语 | 政治 | TOTAL |
+--------+--------+--------+--------+--------+-------+
| 001 | 90 | 92 | 80 | 0 | 262 |
| 002 | 88 | 90 | 75.5 | 0 | 253.5 |
| 003 | 70 | 85 | 90 | 82 | 327 |
| TOTAL | 248 | 267 | 245.5 | 82 | 842.5 |
+--------+--------+--------+--------+--------+-------+
4 rows in set (0.00 sec)
SELECT IFNULL(userid,'TOTAL') AS userid,
SUM(IF(`subject`='语文',score,0)) AS 语文,
SUM(IF(`subject`='数学',score,0)) AS 数学,
SUM(IF(`subject`='英语',score,0)) AS 英语,
SUM(IF(`subject`='政治',score,0)) AS 政治,
SUM(score) AS TOTAL
FROM tb_score
GROUP BY userid WITH ROLLUP;
+--------+--------+--------+--------+--------+-------+
| userid | 语文 | 数学 | 英语 | 政治 | TOTAL |
+--------+--------+--------+--------+--------+-------+
| 001 | 90 | 92 | 80 | 0 | 262 |
| 002 | 88 | 90 | 75.5 | 0 | 253.5 |
| 003 | 70 | 85 | 90 | 82 | 327 |
| TOTAL | 248 | 267 | 245.5 | 82 | 842.5 |
+--------+--------+--------+--------+--------+-------+
4 rows in set, 1 warning (0.00 sec)
SELECT
userid,
GROUP_CONCAT(`subject`, ":", score) AS 成绩
FROM
tb_score
GROUP BY
userid;
+--------+-----------------------------------------+
| userid | 成绩 |
+--------+-----------------------------------------+
| 001 | 语文:90,数学:92,英语:80 |
| 002 | 英语:75.5,数学:90,语文:88 |
| 003 | 语文:70,数学:85,英语:90,政治:82 |
+--------+-----------------------------------------+
3 rows in set (0.00 sec)
group_concat(),手册上说明:该函数返回带有来自一个组的连接的非 NULL 值的字符串结果。
比较抽象,难以理解。通俗点理解,其实是这样的:group_concat()会计算哪些行属于同一组,将属于同一组的列显示出来。要返回哪些列,由函数参数(就是字段名)决定。分组必须有个标准,就是根据group by指定的列进行分组。
结论:group_concat()函数可以很好的建属于同一分组的多个行转化为一个列。
本教程操作环境:windows10系统
C:\Users\Administrator>mysql --version
mysql Ver 14.14 Distrib 5.6.29, for Win64 (x86_64)
DROP TABLE IF EXISTS tb_score;
CREATE TABLE tb_score(
id INT(11) NOT NULL auto_increment,
userid VARCHAR(20) NOT NULL COMMENT '用户id',
subject VARCHAR(20) COMMENT '科目',
score DOUBLE COMMENT '成绩',
PRIMARY KEY(id)
)ENGINE = INNODB DEFAULT CHARSET = utf8;
插入数据
INSERT INTO tb_score(userid,subject,score) VALUES ('001','语文',90);
INSERT INTO tb_score(userid,subject,score) VALUES ('001','数学',92);
INSERT INTO tb_score(userid,subject,score) VALUES ('001','英语',80);
INSERT INTO tb_score(userid,subject,score) VALUES ('002','语文',88);
INSERT INTO tb_score(userid,subject,score) VALUES ('002','数学',90);
INSERT INTO tb_score(userid,subject,score) VALUES ('002','英语',75.5);
INSERT INTO tb_score(userid,subject,score) VALUES ('003','语文',70);
INSERT INTO tb_score(userid,subject,score) VALUES ('003','数学',85);
INSERT INTO tb_score(userid,subject,score) VALUES ('003','英语',90);
INSERT INTO tb_score(userid,subject,score) VALUES ('003','政治',82);