GROUP_CONCAT()函数

创建表person_info,并插入数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

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 王二 王二姐姐

语法:

1
2
3
4
GROUP_CONCAT([DISTINCT] expr [,expr ...]
[ORDER BY {unsigned_integer | col_name | expr}
[ASC | DESC] [,col_name ...]]
[SEPARATOR str_val])

示例:

1
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,并插入数据

1
2
3
4
5
6
7
8
9
10
11
12
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列为表头,展示每个人的成绩

1
2
3
4
5
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列作为表头展示了成绩,但是每条成绩都占用一行,那么如何把同一个人的成绩都在一行展示呢?

1
2
3
4
5
6
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()函数的例子可以看到,表头行字段数学语文政治都是我们提前已经知晓并且手动指定的,那么如果我们事先不知道有哪些表头字段,怎么办呢?

可以通过下面的脚本实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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;

先来看看这句的执行的效果:

1
2
3
4
5
6
7
8
9
10
11
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'max(case when subject = ''',
subject,
''' then mark end) as ',
subject
)
)
FROM
Meeting;

执行结果:

1
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

再通过

1
2
3
SET @sql = CONCAT('SELECT name, ', @sql, ' 
FROM person_grade
GROUP BY name');

拼成完整的SQL

1
2
3
4
5
6
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
⬆︎TOP