mysql 8.0+、postgresql 支持聚合函数内 distinct,sql server 和 sqlite 需子查询去重;mysql 中 distinct 必须紧邻字段且在 order by 前;各库均需注意长度限制、null 处理及大小写敏感性。

MySQL 8.0+ 直接用 GROUP_CONCAT(DISTINCT col ORDER BY col) 就行;PostgreSQL 支持 STRING_AGG(DISTINCT col, ',');SQL Server 和 SQLite 不原生支持 DISTINCT,必须提前子查询去重。
MySQL 8.0+:DISTINCT 必须写在 ORDER BY 前面
很多人写成 GROUP_CONCAT(col ORDER BY col DISTINCT) 或 GROUP_CONCAT(DISTINCT col) ORDER BY col,这两种都错——DISTINCT 是 GROUP_CONCAT 的修饰符,不是独立子句,且必须紧挨字段、位于 ORDER BY 之前。
-
GROUP_CONCAT(DISTINCT name ORDER BY name SEPARATOR '; ')是唯一正确写法 - 默认
group_concat_max_len = 1024,拼接超长会被截断,执行前建议加SET SESSION group_concat_max_len = 10000; - 空值(
NULL)会被忽略,不参与拼接,也不占位
PostgreSQL:STRING_AGG(DISTINCT ...) 是合法语法
PostgreSQL 9.0+ 的 STRING_AGG 确实支持 DISTINCT 关键字,不用绕圈子写子查询——这是和旧资料冲突的最大误区,新版本已修正。
- 正确写法:
STRING_AGG(DISTINCT tag, ',' ORDER BY tag) - 注意:
DISTINCT修饰的是被聚合的表达式,不能写成STRING_AGG(tag, ',') DISTINCT - 如果字段可能为
NULL,DISTINCT会把所有NULL视为同一值,只保留一个;如需排除NULL,加WHERE tag IS NOT NULL
SQL Server 和 SQLite:必须用子查询预去重
这两个系统至今没给聚合函数加 DISTINCT 选项,硬写会报错。别试图在 STRING_AGG 或 GROUP_CONCAT 里塞 DISTINCT,直接失败。
- SQL Server(2017+):
SELECT STRING_AGG(name, ',') WITHIN GROUP (ORDER BY name) FROM (SELECT DISTINCT name FROM t WHERE group_id = ?) AS dedup - SQLite:
SELECT GROUP_CONCAT(name, ',') FROM (SELECT DISTINCT name FROM t WHERE group_id = ? ORDER BY name) - 特别注意:SQLite 的
GROUP_CONCAT支持ORDER BY,但**完全不认DISTINCT关键字**,写了就语法错误
跨数据库兼容写法:为什么推荐子查询而不是依赖内置 DISTINCT
哪怕你当前用的是 MySQL 或 PG,也建议优先考虑子查询去重。因为:
- 逻辑更显式:去重和拼接职责分离,排查时容易定位是数据源重复还是拼接逻辑异常
- 避免隐式行为:比如 MySQL 中
DISTINCT对NULL的处理、PG 中DISTINCT ON和STRING_AGG(DISTINCT)的语义差异 - 迁移友好:一套子查询写法,在 SQL Server、SQLite、甚至老版本 MySQL 上都能跑通
真正容易被忽略的点是:拼接结果长度限制、NULL 是否参与排序、以及不同数据库对“重复”的定义(比如大小写敏感与否)——这些不会在语法报错里体现,但会在数据上线后突然冒出来。










