原创

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_idrole_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 和排序开销。

建议优化策略

  1. 合理控制拼接字段
  2. 避免超大文本拼接
  3. 必要时增加 group_concat_max_len
  4. 尽量减少 ORDER BY
  5. 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 函数进行数据聚合,从而获得更规范的数据格式。

正文到此结束
评论插件初始化中...
Loading...