第一次接触GROUP_CONCAT()是在处理电商订单数据时,当时需要把分散在多行的商品名称合并展示,这个函数简直是我的救命稻草。简单来说,它就像个数据粘合剂,能把分组后的多条记录"粘"成一条字符串。
基础语法其实很直白:
sql复制SELECT
GROUP_CONCAT([DISTINCT] 列名
[ORDER BY 排序字段]
[SEPARATOR '分隔符'])
FROM 表名
GROUP BY 分组字段
实际使用时,我发现这几个参数特别实用:
举个真实案例:有次需要导出用户兴趣标签,每个用户可能有多个标签。用传统方法要先查用户列表,再循环查每个用户的标签,代码写了几十行。改用GROUP_CONCAT后,一行SQL就搞定:
sql复制SELECT
user_id,
GROUP_CONCAT(tag_name SEPARATOR '|') AS user_tags
FROM user_tags
GROUP BY user_id
很多人不知道GROUP_CONCAT其实可以同时合并多列数据。我在做数据迁移项目时,需要把旧系统的地址信息合并导出,用这个特性省了不少事:
sql复制SELECT
customer_id,
GROUP_CONCAT(
CONCAT_WS(':', address_type, province, city)
SEPARATOR '; '
) AS full_address
FROM customer_addresses
GROUP BY customer_id
更高级的玩法是结合JSON函数,直接输出结构化数据。MySQL 5.7+版本可以这样用:
sql复制SELECT
department_id,
CONCAT(
'[',
GROUP_CONCAT(
JSON_OBJECT('id', emp_id, 'name', emp_name)
SEPARATOR ','
),
']'
) AS employees_json
FROM employees
GROUP BY department_id
处理树形结构数据时,配合WITH RECURSIVE实现层级展开。比如组织架构查询:
sql复制WITH RECURSIVE org_tree AS (
SELECT id, name, parent_id, 1 AS level
FROM organization
WHERE parent_id IS NULL
UNION ALL
SELECT o.id, o.name, o.parent_id, ot.level + 1
FROM organization o
JOIN org_tree ot ON o.parent_id = ot.id
)
SELECT
GROUP_CONCAT(
CONCAT(REPEAT(' ', level - 1), name)
ORDER BY level, id
SEPARATOR '\n'
) AS org_chart
FROM org_tree
有次在百万级数据表上使用GROUP_CONCAT,查询直接超时。排查发现三个关键因素:
优化方案:
sql复制-- 会话级设置(立即生效)
SET SESSION group_concat_max_len = 1000000;
-- 全局设置(需重启)
SET GLOBAL group_concat_max_len = 1000000;
当数据量实在太大时,我通常会考虑:
实测对比:在500万条记录的测试中,直接使用GROUP_CONCAT耗时28秒,而分批处理(每批10万条)总耗时仅9秒。
有时需要保留部分重复数据,比如统计商品购买次数:
sql复制SELECT
product_id,
COUNT(*) AS purchase_count,
GROUP_CONCAT(
CASE
WHEN purchase_count > 1 THEN CONCAT(user_id, '(x', purchase_count, ')')
ELSE user_id
END
SEPARATOR ', '
) AS buyer_list
FROM (
SELECT
product_id,
user_id,
COUNT(*) AS purchase_count
FROM orders
GROUP BY product_id, user_id
) t
GROUP BY product_id
通过CASE WHEN实现条件分隔符,比如生成Markdown列表:
sql复制SELECT
GROUP_CONCAT(
CONCAT(
CASE WHEN is_important THEN '❗ ' ELSE '' END,
content
)
SEPARATOR '\n- '
) AS md_list
FROM notes
经过多次项目实战,我总结了这些黄金法则:
有个印象深刻的事故:某次上线忘记调整group_concat_max_len,导致报表系统截断了重要数据。现在我的检查清单里一定会包含这一项。
对于高并发场景,建议在应用启动时统一设置会话参数:
sql复制-- 初始化数据库连接时执行
SET SESSION group_concat_max_len = 1000000;
SET SESSION sql_mode = 'STRICT_TRANS_TABLES';
最后分享一个实用技巧:在开发环境可以用EXPLAIN ANALYZE查看GROUP_CONCAT的实际内存使用情况,这对性能调优特别有帮助。