在Oracle数据库的业务查询场景中,将同一分组下的多行字符串字段值合并为单个字符串是常见需求,比如汇总同一订单的所有商品名称、统计同一部门的所有员工工号等。不同的实现方式在兼容性、性能和功能支持上存在差异,开发者需要结合实际情况选择。

Oracle多行记录合并的常用方法
1. 使用wmsys.wm_concat函数
wmsys.wm_concat是Oracle早期提供的字符串聚合函数,属于内部函数,使用门槛较低,语法简单。它默认会将合并后的字符串用逗号分隔,不需要额外指定分隔符参数。
示例表结构如下,我们基于该表演示合并操作:
| 部门ID | 员工姓名 |
|---|---|
| 10 | 张三 |
| 10 | 李四 |
| 20 | 王五 |
使用wmsys.wm_concat合并同一部门的员工姓名:
-- 按部门ID分组,合并员工姓名 SELECT 部门ID, wmsys.wm_concat(员工姓名) AS 员工列表 FROM 员工表 GROUP BY 部门ID;
该方法的优点是语法简单,兼容Oracle 10g及以上大部分版本;缺点是它是Oracle未公开的内部函数,在12c及以后版本中已经被废弃,部分新版本中可能无法使用,且不支持自定义分隔符,合并后的字符串顺序是随机的。
2. 使用LISTAGG函数
LISTAGG是Oracle 11g R2版本官方推出的字符串聚合函数,属于标准功能,稳定性和兼容性更好,支持自定义分隔符和排序规则。
基础语法如下:
LISTAGG(合并字段, 分隔符) WITHIN GROUP (ORDER BY 排序字段)
同样基于上述员工表,使用LISTAGG实现按部门合并员工姓名,用逗号分隔,按姓名升序排列:
-- 按部门ID分组,合并员工姓名,用逗号分隔,按姓名升序排序
SELECT 部门ID,
LISTAGG(员工姓名, ',') WITHIN GROUP (ORDER BY 员工姓名) AS 员工列表
FROM 员工表
GROUP BY 部门ID;
如果需要处理合并后字符串过长的情况,可以使用LISTAGG的截断功能,避免超过VARCHAR2的最大长度限制:
-- 合并字符串超过100字符时截断并添加省略号
SELECT 部门ID,
LISTAGG(员工姓名, ',') WITHIN GROUP (ORDER BY 员工姓名)
ON OVERFLOW TRUNCATE '...'
AS 员工列表
FROM 员工表
GROUP BY 部门ID;
LISTAGG的优点是官方支持,功能完善,支持自定义分隔符和排序,性能也优于wmsys.wm_concat;缺点是仅支持Oracle 11g R2及以上版本,低版本数据库无法使用。
3. 使用XMLAGG函数
XMLAGG是基于XML处理的聚合函数,兼容性较好,支持Oracle 9i及以上版本,也支持自定义分隔符和排序,适合低版本数据库使用。
实现示例如下,合并同一部门的员工姓名,用逗号分隔:
-- 按部门ID分组,使用XMLAGG合并员工姓名
SELECT 部门ID,
RTRIM(XMLAGG(XMLELEMENT(e, 员工姓名 || ',') ORDER BY 员工姓名).EXTRACT('//text()').getClobVal(), ',') AS 员工列表
FROM 员工表
GROUP BY 部门ID;
上述代码中,XMLELEMENT用于将每个员工姓名拼接上分隔符,XMLAGG进行聚合,EXTRACT提取文本内容,RTRIM用于去掉最后一个多余的逗号。该方法的优点是兼容性好,支持低版本Oracle,也支持自定义分隔符和排序;缺点是语法相对复杂,可读性较差,且合并后的结果类型是CLOB,如果需要VARCHAR2类型还需要额外转换。
4. 使用自定义聚合函数
如果上述内置函数都无法满足需求,比如需要支持特殊的聚合逻辑,还可以自定义聚合函数实现多行字符串合并。首先需要定义一个对象类型来实现聚合逻辑:
-- 定义字符串聚合的对象类型 CREATE OR REPLACE TYPE str_agg_type AS OBJECT ( total CLOB, -- 初始化方法 STATIC FUNCTION odciaggregateinitialize(sctx IN OUT str_agg_type) RETURN NUMBER, -- 迭代方法,处理每一行数据 MEMBER FUNCTION odciaggregateiterate(self IN OUT str_agg_type, value IN VARCHAR2) RETURN NUMBER, -- 终止方法,返回最终结果 MEMBER FUNCTION odciaggregateterminate(self IN str_agg_type, returnvalue OUT CLOB, flags IN NUMBER) RETURN NUMBER, -- 合并方法,处理并行聚合 MEMBER FUNCTION odciaggregatemerge(self IN OUT str_agg_type, ctx2 IN str_agg_type) RETURN NUMBER ); /
然后实现该对象类型的方法:
CREATE OR REPLACE TYPE BODY str_agg_type IS
-- 初始化
STATIC FUNCTION odciaggregateinitialize(sctx IN OUT str_agg_type) RETURN NUMBER IS
BEGIN
sctx := str_agg_type(NULL);
RETURN odciconst.success;
END odciaggregateinitialize;
-- 迭代处理每一行,拼接字符串
MEMBER FUNCTION odciaggregateiterate(self IN OUT str_agg_type, value IN VARCHAR2) RETURN NUMBER IS
BEGIN
IF self.total IS NULL THEN
self.total := value;
ELSE
self.total := self.total || ',' || value;
END IF;
RETURN odciconst.success;
END odciaggregateiterate;
-- 返回最终结果
MEMBER FUNCTION odciaggregateterminate(self IN str_agg_type, returnvalue OUT CLOB, flags IN NUMBER) RETURN NUMBER IS
BEGIN
returnvalue := self.total;
RETURN odciconst.success;
END odciaggregateterminate;
-- 合并并行结果
MEMBER FUNCTION odciaggregatemerge(self IN OUT str_agg_type, ctx2 IN str_agg_type) RETURN NUMBER IS
BEGIN
IF ctx2.total IS NOT NULL THEN
IF self.total IS NULL THEN
self.total := ctx2.total;
ELSE
self.total := self.total || ',' || ctx2.total;
END IF;
END IF;
RETURN odciconst.success;
END odciaggregatemerge;
END;
/
最后创建自定义聚合函数:
-- 创建自定义聚合函数 CREATE OR REPLACE FUNCTION str_agg(input VARCHAR2) RETURN CLOB AGGREGATE USING str_agg_type; /
使用自定义函数合并员工姓名:
-- 使用自定义聚合函数合并字符串 SELECT 部门ID, str_agg(员工姓名) AS 员工列表 FROM 员工表 GROUP BY 部门ID;
自定义聚合函数的优点是灵活性极高,可以根据需求定制聚合逻辑,兼容性也较好;缺点是实现步骤繁琐,需要创建类型和函数,维护成本较高。
不同方法的适用场景总结
- 如果使用Oracle 11g R2及以上版本,优先选择LISTAGG函数,功能完善且官方支持。
- 如果使用Oracle 11g R2以下版本,且没有特殊分隔符需求,可以选择wmsys.wm_concat,但要注意新版本兼容性问题。
- 如果使用低版本Oracle,且需要自定义分隔符和排序,可以选择XMLAGG函数。
- 如果有特殊的聚合逻辑,内置函数无法满足,可以选择自定义聚合函数。
Oracle多行记录合并字符串聚合wmsys.wm_concatlistagg修改时间:2026-06-16 00:39:38