MySQL GROUP_CONCAT 函数详解与实战用法
GROUP_CONCAT 函数概述
在关系型数据库的查询过程中,经常需要将多行数据合并为一行进行展示。例如,一个用户可能拥有多个角色,一个订单可能包含多个商品。如果直接使用普通查询,通常会返回多行结果,而在某些业务场景下,需要将这些结果聚合为一个字符串进行展示或处理。
MySQL 提供了 GROUP_CONCAT 聚合函数,用于将多个行值拼接为一个字符串返回。该函数常与 GROUP BY 一起使用,用于在分组结果中对某一列的数据进行拼接。
GROUP_CONCAT 是 MySQL 中非常重要的字符串聚合函数,在报表统计、数据展示、复杂查询优化等场景中被广泛使用。
MySQL 官方语法定义如下:
GROUP_CONCAT([DISTINCT] expr
[ORDER BY {col_name | expr} [ASC | DESC]]
[SEPARATOR str_val])
参数说明:
| 参数 | 说明 |
|---|---|
| expr | 需要拼接的字段 |
| DISTINCT | 去重 |
| ORDER BY | 指定拼接顺序 |
| SEPARATOR | 指定分隔符 |
函数返回值为字符串类型,如果没有匹配行则返回 NULL。
GROUP_CONCAT 的基本使用
首先创建一个简单示例表,用于演示 GROUP_CONCAT 的基本功能。
CREATE TABLE user_role
(
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL,
role_name VARCHAR(64) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
插入测试数据:
INSERT INTO user_role (user_id, role_name) VALUES
(1, 'admin'),
(1, 'editor'),
(1, 'developer'),
(2, 'admin'),
(2, 'auditor'),
(3, 'guest');
查询每个用户拥有的角色列表:
SELECT
user_id,
GROUP_CONCAT(role_name)
FROM user_role
GROUP BY user_id;
返回结果:
| user_id | GROUP_CONCAT(role_name) |
|---|---|
| 1 | admin,editor,developer |
| 2 | admin,auditor |
| 3 | guest |
默认情况下:
- 分隔符为 逗号
, - 拼接顺序为 查询结果顺序
- 不会自动排序
使用 SEPARATOR 指定分隔符
在实际业务中,逗号并不一定符合需求,可以使用 SEPARATOR 指定自定义分隔符。
示例:
SELECT
user_id,
GROUP_CONCAT(role_name SEPARATOR '|')
FROM user_role
GROUP BY user_id;
结果:
| user_id | roles | ||
|---|---|---|---|
| 1 | admin | editor | developer |
| 2 | admin | auditor | |
| 3 | guest |
还可以使用更复杂的分隔符,例如:
GROUP_CONCAT(role_name SEPARATOR ' / ')
结果:
admin / editor / developer
使用 ORDER BY 控制拼接顺序
默认情况下,GROUP_CONCAT 不保证拼接顺序。如果希望结果有固定排序,需要使用 ORDER BY。
例如按照角色名称排序:
SELECT
user_id,
GROUP_CONCAT(role_name ORDER BY role_name)
FROM user_role
GROUP BY user_id;
降序排序:
SELECT
user_id,
GROUP_CONCAT(role_name ORDER BY role_name DESC)
FROM user_role
GROUP BY user_id;
同时指定排序和分隔符:
SELECT
user_id,
GROUP_CONCAT(role_name ORDER BY role_name DESC SEPARATOR ';')
FROM user_role
GROUP BY user_id;
输出示例:
editor;developer;admin
使用 DISTINCT 进行去重
在某些情况下,表中可能存在重复数据,需要去重后再拼接。
例如插入重复数据:
INSERT INTO user_role (user_id, role_name) VALUES
(1, 'admin');
查询:
SELECT
user_id,
GROUP_CONCAT(DISTINCT role_name)
FROM user_role
GROUP BY user_id;
结果:
admin,editor,developer
去重只作用于拼接字段。
GROUP_CONCAT 多字段拼接
GROUP_CONCAT 可以拼接表达式,因此不仅限于单列。
例如拼接 user_id 与 role_name。
SELECT
user_id,
GROUP_CONCAT(CONCAT(user_id, ':', role_name))
FROM user_role
GROUP BY user_id;
结果:
1:admin,1:editor,1:developer
复杂表达式示例:
GROUP_CONCAT(
CONCAT('[', role_name, ']')
)
结果:
[admin],[editor],[developer]
GROUP_CONCAT 与 GROUP BY 的结合
GROUP_CONCAT 本质上是 聚合函数,通常与 GROUP BY 一起使用。
例如一个用户表和角色表。
创建用户表:
CREATE TABLE user
(
id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(64) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
插入数据:
INSERT INTO user (id, username) VALUES
(1, 'alice'),
(2, 'bob'),
(3, 'charlie');
关联查询:
SELECT
u.id,
u.username,
GROUP_CONCAT(r.role_name)
FROM user u
LEFT JOIN user_role r
ON u.id = r.user_id
GROUP BY u.id, u.username;
结果:
| id | username | roles |
|---|---|---|
| 1 | alice | admin,editor,developer |
| 2 | bob | admin,auditor |
| 3 | charlie | guest |
这是典型的 一对多数据聚合。
GROUP_CONCAT 长度限制
GROUP_CONCAT 有默认长度限制。
查看当前配置:
SHOW VARIABLES LIKE 'group_concat_max_len';
默认值通常为:
1024
当拼接字符串超过该长度时,结果会被 截断。
例如:
admin,editor,developer,...
修改限制
会话级别:
SET SESSION group_concat_max_len = 10240;
全局级别:
SET GLOBAL group_concat_max_len = 10240;
建议在报表或大数据聚合场景中提高该值。
GROUP_CONCAT 在复杂查询中的应用
构建 JSON 结构
在 MySQL 5.7 以前,没有 JSON 聚合函数时,经常使用 GROUP_CONCAT 构建 JSON。
示例:
SELECT
user_id,
CONCAT(
'[',
GROUP_CONCAT(CONCAT('"', role_name, '"')),
']'
) AS roles_json
FROM user_role
GROUP BY user_id;
输出:
["admin","editor","developer"]
但 MySQL 5.7+ 推荐使用:
JSON_ARRAYAGG()
构建动态 SQL
在某些情况下需要生成动态 SQL,例如拼接字段名。
示例:
SELECT
GROUP_CONCAT(column_name)
FROM information_schema.columns
WHERE table_name = 'user';
结果:
id,username,email,create_time
可以用于生成动态查询语句。
GROUP_CONCAT 在统计报表中的应用
例如统计订单商品。
创建订单表:
CREATE TABLE orders
(
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT,
create_time DATETIME
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
创建订单商品表:
CREATE TABLE order_item
(
id BIGINT PRIMARY KEY AUTO_INCREMENT,
order_id BIGINT,
product_name VARCHAR(128)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
插入数据:
INSERT INTO orders (id,user_id,create_time) VALUES
(1,1,NOW()),
(2,2,NOW());
INSERT INTO order_item (order_id,product_name) VALUES
(1,'iPhone'),
(1,'AirPods'),
(1,'iPad'),
(2,'MacBook'),
(2,'Magic Mouse');
查询订单商品列表:
SELECT
o.id,
GROUP_CONCAT(i.product_name ORDER BY i.product_name)
FROM orders o
LEFT JOIN order_item i
ON o.id = i.order_id
GROUP BY o.id;
结果:
1 AirPods,iPad,iPhone
2 MacBook,Magic Mouse
GROUP_CONCAT 性能注意事项
虽然 GROUP_CONCAT 非常方便,但在大数据量场景下需要注意性能问题。
内存消耗
GROUP_CONCAT 在执行过程中需要在内存中维护聚合结果。
如果拼接数据量很大,会导致:
- 内存消耗增加
- 查询性能下降
排序开销
当使用 ORDER BY 时:
GROUP_CONCAT(... ORDER BY ...)
MySQL 需要对每个分组进行排序。
在大数据量情况下会明显增加 CPU 和排序开销。
建议优化策略
- 合理控制拼接字段
- 避免超大文本拼接
- 必要时增加
group_concat_max_len - 尽量减少
ORDER BY - 对
GROUP BY字段建立索引
GROUP_CONCAT 与 JSON_ARRAYAGG 对比
MySQL 5.7+ 引入 JSON 聚合函数。
| 函数 | 返回类型 |
|---|---|
| GROUP_CONCAT | 字符串 |
| JSON_ARRAYAGG | JSON |
示例:
SELECT
user_id,
JSON_ARRAYAGG(role_name)
FROM user_role
GROUP BY user_id;
结果:
["admin","editor","developer"]
相比之下:
GROUP_CONCAT更灵活JSON_ARRAYAGG更标准
GROUP_CONCAT 常见问题
结果被截断
原因:
group_concat_max_len 太小
解决:
SET SESSION group_concat_max_len = 100000;
拼接顺序不正确
解决:
GROUP_CONCAT(... ORDER BY ...)
出现重复值
解决:
GROUP_CONCAT(DISTINCT column)
总结
GROUP_CONCAT 是 MySQL 中非常实用的字符串聚合函数,主要用于将多行数据合并为一行字符串。其核心能力包括:
- 多行字符串拼接
- 自定义分隔符
- 分组聚合
- 去重处理
- 排序控制
在实际项目中,GROUP_CONCAT 经常用于:
- 一对多数据展示
- 报表统计
- 动态 SQL 构建
- JSON 数据拼接
但在大规模数据场景中需要注意长度限制、排序开销以及内存消耗等问题。合理使用索引、控制数据规模,可以显著提升查询性能。
在 MySQL 5.7+ 环境下,如果需要结构化结果,也可以结合 JSON_ARRAYAGG 等 JSON 函数进行数据聚合,从而获得更规范的数据格式。