STRING_AGG 在不同数据库中的用法与差异

在日常业务开发中,我们经常需要将同一分组下的多个值拼接成一个字符串 ,并用特定分隔符分隔。

在 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 BYDISTINCT(需子查询)。

  • 简单高效,适合小规模拼接。


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 逻辑。