在日常业务开发中,我们经常需要将同一分组下的多个值拼接成一个字符串 ,并用特定分隔符分隔。
在 SQL Server 2017 之后,这个需求有了官方函数 STRING_AGG
,相比之前的 STUFF + FOR XML PATH
写法更直观、更高效。
然而,在跨数据库开发时,不同数据库对"字符串聚合"的实现方式、函数名、排序能力等存在差异。本文将详细介绍 STRING_AGG
及各数据库的等价函数用法。
1. SQL Server --- STRING_AGG
基本语法
STRING_AGG ( expression, separator )
[ WITHIN GROUP (ORDER BY order_expression [ASC|DESC]) ] -- 2022+ 支持
示例
-- 拼接所有姓名,用逗号分隔
SELECT STRING_AGG(name, ',') AS names
FROM employees;
-- 按部门分组拼接,并按姓名排序(SQL Server 2022+)
SELECT department_id,
STRING_AGG(name, ',') WITHIN GROUP (ORDER BY name)
FROM employees
GROUP BY department_id;
特点
-
2017+ 提供基础功能。
-
2022+ 支持
WITHIN GROUP (ORDER BY ...)
直接排序。 -
不支持直接
DISTINCT
,需用子查询。
2. PostgreSQL --- string_agg
语法
string_agg ( expression, separator [ ORDER BY sort_expression ] )
示例
-- 拼接所有姓名,用逗号分隔并排序
SELECT department_id,
string_agg(name, ',' ORDER BY name) AS names
FROM employees
GROUP BY department_id;
特点
-
排序能力更灵活,可在函数内直接
ORDER BY
。 -
不支持直接
DISTINCT
,需子查询去重。
3. MySQL / MariaDB --- GROUP_CONCAT
语法
GROUP_CONCAT([DISTINCT] expression ORDER BY sort_expression SEPARATOR 'separator')
示例
-- 拼接姓名并排序
SELECT department_id,
GROUP_CONCAT(DISTINCT name ORDER BY name SEPARATOR ',') AS names
FROM employees
GROUP BY department_id;
特点
-
支持
DISTINCT
和排序。 -
默认最大长度 1024,需调整:
SET SESSION group_concat_max_len = 1000000;
-
MariaDB 与 MySQL 用法一致。
4. Oracle / DB2 --- LISTAGG
语法
LISTAGG(expression, separator) WITHIN GROUP (ORDER BY sort_expression)
示例
-- Oracle / DB2 拼接示例
SELECT department_id,
LISTAGG(name, ',') WITHIN GROUP (ORDER BY name) AS names
FROM employees
GROUP BY department_id;
特点
-
必须指定
WITHIN GROUP (ORDER BY ...)
。 -
Oracle 12c 之前有 4000 字节限制(CLOB 可突破)。
-
19c+ 支持
ON OVERFLOW
处理超长结果。
5. SQLite --- group_concat
语法
group_concat(expression, separator)
示例
-- 拼接姓名(不支持函数内排序)
SELECT department_id,
group_concat(name, ',') AS names
FROM employees
GROUP BY department_id;
特点
-
不支持
ORDER BY
和DISTINCT
(需子查询)。 -
简单高效,适合小规模拼接。
6. DuckDB --- string_agg
语法
string_agg(expression, separator [ ORDER BY sort_expression ])
示例
SELECT department_id,
string_agg(name, ',' ORDER BY name) AS names
FROM employees
GROUP BY department_id;
特点
-
语法几乎与 PostgreSQL 一致。
-
原生支持排序。
7. 对比表
数据库 | 函数名 | 排序支持 | DISTINCT | 版本要求 |
---|---|---|---|---|
SQL Server | STRING_AGG | 2022+ 支持 | 否 | 2017+ |
PostgreSQL | string_agg | 是 | 否 | 早期版本 |
MySQL/MariaDB | GROUP_CONCAT | 是 | 是 | 早期版本 |
Oracle | LISTAGG | 是 | 否 | 11g+ |
DB2 | LISTAGG | 是 | 否 | 9.7+ |
SQLite | group_concat | 否 | 否 | 早期版本 |
DuckDB | string_agg | 是 | 否 | 早期版本 |
8. 跨数据库适配建议
对于多数据库的 SaaS 系统,可以在 ORM 层封装一个统一函数,比如:
-- 伪代码
STRING_JOIN(expr, sep, order_by=null, distinct=false)
然后按数据库类型映射:
-
SQL Server →
STRING_AGG
-
PostgreSQL →
string_agg
-
MySQL →
GROUP_CONCAT
-
Oracle / DB2 →
LISTAGG
-
SQLite →
group_concat
这样能一次开发,多数据库运行,减少 SQL 改动量。
9. 总结
-
STRING_AGG
是 SQL Server 从 2017 开始提供的字符串聚合函数,大大简化了多行拼接的写法。 -
不同数据库功能类似,但名字、排序能力、是否支持 DISTINCT、结果长度限制不同。
-
在跨数据库项目中,应做好 SQL 封装,避免重复维护多套 SQL 逻辑。