多对多关系与扁平化需求
在数据库设计中,多对多关系通常通过三张表实现:两张实体表加一张中间关联表。比如学生表和课程表,一个学生可以选多门课程,一门课程也可以被多个学生选择,二者通过学生课程关联表建立联系。这种结构在存储时非常规范,但在查询时如果需要将某个学生选择的所有课程名称合并展示成一条记录,就需要对多对多关系进行扁平化处理。

传统查询的问题
如果不做扁平化处理,查询学生对应的课程会返回多条记录,比如学生A选了数学、语文、英语三门课,查询结果会有三行,每行对应一门课程。这种结果在部分业务场景中不符合需求,比如前端列表需要一行展示学生的所有课程信息,就需要将多行合并成一行。
使用GROUP_CONCAT实现扁平化(MySQL、SQLite)
GROUP_CONCAT是MySQL和SQLite支持的字符串聚合函数,它可以将分组后的多个值按照指定分隔符拼接成一个字符串,非常适合处理多对多关系的扁平化。
基础表结构示例
我们首先创建三张测试表,模拟学生和课程的关联关系:
-- 学生表
CREATE TABLE student (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL
);
-- 课程表
CREATE TABLE course (
id INT PRIMARY KEY AUTO_INCREMENT,
course_name VARCHAR(50) NOT NULL
);
-- 学生课程关联表
CREATE TABLE student_course (
student_id INT,
course_id INT,
PRIMARY KEY (student_id, course_id),
FOREIGN KEY (student_id) REFERENCES student(id),
FOREIGN KEY (course_id) REFERENCES course(id)
);
创建包含扁平化结果的视图
我们可以通过GROUP_CONCAT函数创建视图,将每个学生的课程名称拼接成逗号分隔的字符串:
CREATE VIEW student_course_flat AS
SELECT
s.id AS student_id,
s.name AS student_name,
GROUP_CONCAT(c.course_name SEPARATOR ',') AS course_list
FROM student s
LEFT JOIN student_course sc ON s.id = sc.student_id
LEFT JOIN course c ON sc.course_id = c.id
GROUP BY s.id, s.name;
上述视图中,我们通过LEFT JOIN关联三张表,然后按照学生ID和姓名分组,使用GROUP_CONCAT将同一个学生的所有课程名称拼接起来,默认分隔符是逗号,也可以通过SEPARATOR参数自定义分隔符。
查询视图结果
直接查询视图可以得到扁平化后的结果:
SELECT * FROM student_course_flat;
如果学生A的ID是1,选择了数学、语文、英语三门课,查询结果中course_list字段的值会是“数学,语文,英语”。
使用STRING_AGG实现扁平化(PostgreSQL、SQL Server)
STRING_AGG是PostgreSQL和SQL Server支持的字符串聚合函数,功能和GROUP_CONCAT类似,语法上略有差异。
PostgreSQL中的实现
在PostgreSQL中,创建同样的视图可以使用以下语句:
CREATE VIEW student_course_flat AS
SELECT
s.id AS student_id,
s.name AS student_name,
STRING_AGG(c.course_name, ',') AS course_list
FROM student s
LEFT JOIN student_course sc ON s.id = sc.student_id
LEFT JOIN course c ON sc.course_id = c.id
GROUP BY s.id, s.name;
STRING_AGG的第一个参数是要拼接的字段,第二个参数是分隔符,用法和GROUP_CONCAT的SEPARATOR参数类似。
SQL Server中的实现
SQL Server从2017版本开始支持STRING_AGG函数,使用方式和PostgreSQL基本一致:
CREATE VIEW student_course_flat AS
SELECT
s.id AS student_id,
s.name AS student_name,
STRING_AGG(c.course_name, ',') AS course_list
FROM student s
LEFT JOIN student_course sc ON s.id = sc.student_id
LEFT JOIN course c ON sc.course_id = c.id
GROUP BY s.id, s.name;
两种函数的注意事项
- 如果分组后没有匹配的关联记录,GROUP_CONCAT和STRING_AGG都会返回NULL,如果需要返回空字符串,可以使用COALESCE函数处理,比如COALESCE(STRING_AGG(c.course_name, ','), '')。
- GROUP_CONCAT在MySQL中有长度限制,默认是1024字符,如果拼接的字符串过长,可以通过调整group_concat_max_len参数修改限制。
- 两种函数都支持排序,比如GROUP_CONCAT(c.course_name ORDER BY c.course_name SEPARATOR ',')可以按照课程名称升序拼接结果。
适用场景总结
当需要在SQL视图中处理多对多关系的扁平化时,GROUP_CONCAT和STRING_AGG是非常合适的选择。如果是MySQL或SQLite数据库,优先使用GROUP_CONCAT;如果是PostgreSQL或SQL Server数据库,优先使用STRING_AGG。这种方式可以将复杂的多表关联查询逻辑封装到视图中,简化上层业务的查询操作,同时提升查询效率。
SQL视图多对多关系GROUP_CONCATSTRING_AGG关系扁平化修改时间:2026-06-25 18:48:17