GROUP_CONCAT()函数
创建表person_info
,并插入数据
CREATE TABLE `person_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(100) DEFAULT NULL,
`family` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
INSERT INTO niffler.person_info (name, family) VALUES('张三', '张三爸');
INSERT INTO niffler.person_info (name, family) VALUES('张三', '张三妈');
INSERT INTO niffler.person_info (name, family) VALUES('李四', '李四爸');
INSERT INTO niffler.person_info (name, family) VALUES('李四', '李四妈');
INSERT INTO niffler.person_info (name, family) VALUES('李四', '李四大哥');
INSERT INTO niffler.person_info (name, family) VALUES('王二', '王二爷爷');
INSERT INTO niffler.person_info (name, family) VALUES('王二', '王二姐姐');
id | name | family |
---|---|---|
1 | 张三 | 张三爸 |
2 | 张三 | 张三妈 |
3 | 李四 | 李四爸 |
4 | 李四 | 李四妈 |
5 | 李四 | 李四大哥 |
6 | 王二 | 王二爷爷 |
7 | 王二 | 王二姐姐 |
语法:
GROUP_CONCAT([DISTINCT] expr [,expr ...]
[ORDER BY {unsigned_integer | col_name | expr}
[ASC | DESC] [,col_name ...]]
[SEPARATOR str_val])
示例:
SELECT name, GROUP_CONCAT(family SEPARATOR '&') AS familys FROM person_info GROUP BY name;
结果:
可以看看我上篇写的关于GROUP_CONCAT()的文章【笔记】MySQL数据库GROUP_CONCAT() 函数输出结果的长度限制
CASE函数
MySQL中的CASE表达式不是行转列函数。它是一种条件表达式,用于根据条件对数据进行选择、计算和转换。
然而,你可以使用CASE表达式来实现行转列的效果。通过在CASE表达式中定义不同的条件和相应的结果,你可以将行的数据按照不同的条件拆分到不同的列中。
创建表person_grade
,并插入数据
CREATE TABLE `person_grade` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(100) DEFAULT NULL,
`subject` varchar(100) DEFAULT NULL,
`mark` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
INSERT INTO niffler.person_grade (name, subject, mark) VALUES('张三', '数学', 66);
INSERT INTO niffler.person_grade (name, subject, mark) VALUES('张三', '语文', 99);
INSERT INTO niffler.person_grade (name, subject, mark) VALUES('李四', '数学', 77);
INSERT INTO niffler.person_grade (name, subject, mark) VALUES('李四', '政治', 80);
id | name | subject | mark |
---|---|---|---|
1 | 张三 | 数学 | 66 |
2 | 张三 | 语文 | 99 |
3 | 李四 | 数学 | 77 |
4 | 李四 | 政治 | 80 |
以subject列为表头,展示每个人的成绩
select name ,
(case subject when '数学' then mark end) as '数学' ,
(case subject when '语文' then mark end) as '语文' ,
(case subject when '政治' then mark end) as '政治'
from person_grade;
结果
确实是按照subject列作为表头展示了成绩,但是每条成绩都占用一行,那么如何把同一个人的成绩都在一行展示呢?
select name ,
MAX(case subject when '数学' then mark end) as '数学' ,
MAX(case subject when '语文' then mark end) as '语文' ,
MAX(case subject when '政治' then mark end) as '政治'
from person_grade
group by name ;
结果
上面的脚本,把MAX函数换成SUM函数,效果一样;除了CASE函数,也可使用IF函数实现行转列的效果。
GROUP_CONCAT()和CASE()结合实现动态行转列
通过CASE()函数的例子可以看到,表头行字段数学
、语文
、政治
都是我们提前已经知晓并且手动指定的,那么如果我们事先不知道有哪些表头字段,怎么办呢?
可以通过下面的脚本实现:
SET @sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'max(case when subject = ''',
subject,
''' then mark end) ',
subject
)
) INTO @sql
FROM
Meeting;
SET @sql = CONCAT('SELECT Meeting_id, ', @sql, '
FROM Meeting
GROUP BY Meeting_id');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
# 定义预处理语句
# PREPARE stmt_name FROM preparable_stmt;
# 执行预处理语句
# EXECUTE stmt_name [USING @var_name [, @var_name] ...];
# 删除(释放)定义
# {DEALLOCATE | DROP} PREPARE stmt_name;
先来看看这句的执行的效果:
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'max(case when subject = ''',
subject,
''' then mark end) as ',
subject
)
)
FROM
Meeting;
执行结果:
max(case when subject = '政治' then mark end) as 政治,max(case when subject = '数学' then mark end) as 数学,max(case when subject = '语文' then mark end) as 语文
是不是已经看出来了,就是为了动态得到行,不是由我们手动指定的,而是通过脚本自动生成、拼接而来。
然后把拼接后的结果 INTO @sql
再通过
SET @sql = CONCAT('SELECT name, ', @sql, '
FROM person_grade
GROUP BY name');
拼成完整的SQL
SELECT name,
max(case when subject = '政治' then mark end) as 政治,
max(case when subject = '数学' then mark end) as 数学,
max(case when subject = '语文' then mark end) as 语文
FROM person_grade
GROUP BY name