MySQL 8.0+ 最可靠方案是JSON_TABLE:先用CONCAT和REPLACE将'a,b,c'转为'["a","b","c"]',再通过PATH '$[*]'展开;5.7需用数字序列配合SUBSTRING_INDEX模拟拆分,并过滤空值。

MySQL 没有 split() 函数,也不支持直接把逗号分隔的字符串“一键转多行”,但拼接和分割都有成熟、可落地的方案。关键看版本(5.7 还是 8.0+)和你要解决的具体问题:是把多行合并成一行?还是把一个字段里的逗号串拆成多行或多个列?选错方法容易返回空、截断、或漏值。
用 GROUP_CONCAT 把多行拼成一行(带分隔符)
这是最常用也最稳定的拼接方式,尤其适合聚合查询场景。
-
GROUP_CONCAT默认用逗号分隔,可用SEPARATOR自定义,比如SEPARATOR '|' - 不加
DISTINCT会保留重复值;加了就去重,但注意性能开销 - 结果长度受
group_concat_max_len系统变量限制,默认 1024 字符,超长会被截断——必须提前SET SESSION group_concat_max_len = 1000000; - 排序很重要:
ORDER BY field_name DESC要写在GROUP_CONCAT内部,不是外面的ORDER BY
示例:SELECT dept_id, GROUP_CONCAT(emp_name ORDER BY hire_date DESC SEPARATOR ' → ') AS staff_list FROM employees GROUP BY dept_id;
用 SUBSTRING_INDEX 提取逗号串中的第 N 个值(固定位置)
适用于你知道最多有几个值、且只需取前几个字段的场景,比如把 "a,b,c,d" 拆成四列。
- 提取第 1 个:
SUBSTRING_INDEX(col, ',', 1) - 提取第 2 个:
SUBSTRING_INDEX(SUBSTRING_INDEX(col, ',', 2), ',', -1) - 提取第 3 个:
SUBSTRING_INDEX(SUBSTRING_INDEX(col, ',', 3), ',', -1) - 如果某行只有 2 个值,取第 3 个会重复返回最后一个值(如
"x,y"→ 第 3 个仍是"y"),需配合IF或CASE判断原始值数量 - 分隔符含特殊字符(如
;、|、甚至空格)也能用,但注意delim参数要完全匹配
常见错误:漏掉嵌套的 -1,写成 SUBSTRING_INDEX(col, ',', 2) 会返回 "a,b" 而不是 "b"。
使用 JSON Schema 验证 JSON 数据,从示例 JSON 生成 schema,并将其转换为 TypeScript 接口、Python 数据类或 Markdown 文档。
MySQL 8.0+ 用 JSON_TABLE 把逗号串真正转成多行
这是目前唯一能“动态展开”任意长度逗号串的原生方案,比模拟数字序列靠谱得多。
- 必须先把字符串转成合法 JSON 数组格式:
CONCAT('["', REPLACE(col, ',', '","'), '"]') - 再套
JSON_TABLE:例如JSON_TABLE(CONCAT('["', REPLACE(col, ',', '","'), '"]'), '$[*]' COLUMNS (val VARCHAR(100) PATH '$')) AS jt - 原始字段含双引号时,得先
REPLACE(col, '"', '\"'),否则 JSON 解析失败 - 如果原始字符串为空或全是空格,
REPLACE后可能生成[""],导致多出空行,建议外层加WHERE TRIM(col) != ''
示例片段:SELECT t.id, jt.val FROM tbl t, JSON_TABLE(CONCAT('["', REPLACE(t.tags, ',', '","'), '"]'), '$[*]' COLUMNS (val VARCHAR(255) PATH '$')) AS jt WHERE TRIM(t.tags) != '';
MySQL 5.7 只能靠数字序列 + SUBSTRING_INDEX 模拟拆分
没有 JSON_TABLE,就得手动构造行号,本质是“横向展开再过滤”。它不优雅,但兼容性好。
- 通常借助
information_schema.tables或自建数字表(如(SELECT 1 n UNION SELECT 2 UNION SELECT 3 ...))生成 1~N 的序号 - 然后用
SUBSTRING_INDEX(SUBSTRING_INDEX(col, ',', n), ',', -1)取第 n 个值 - 必须预估最大分割数(比如最多 6 个标签),否则超出部分永远拿不到
- 每行会生成 N 行结果,其中很多是重复或空值,要用
HAVING val != ''或WHERE LENGTH(val) > 0过滤 - 性能随 N 增大明显下降,N=10 时已比 8.0+ 的
JSON_TABLE慢 3 倍以上
真正麻烦的不是语法,而是:你得确认业务是否允许“最多只处理 5 个值”——一旦上线后数据变长,旧逻辑就 silently 失效。

















