Oracle 19c+中WM_CONCAT已被移除,必须改用LISTAGG(首选)、XMLAGG(兼容旧版)或高风险自定义函数;LISTAGG需WITHIN GROUP子句,支持DISTINCT去重;XMLAGG冗长且需手动处理分隔符;所有WM_CONCAT(DISTINCT...)均非法。

Oracle 19c 及更高版本中,WM_CONCAT 已被彻底移除,直接调用会报 ORA-00904: "WM_CONCAT": invalid identifier 错误。必须改用官方支持的替代方案,首选 LISTAGG。
LISTAGG 是最直接、最安全的替代方案
它原生支持分隔符、排序、去重(19c+),且性能优于自定义函数。关键点在于语法结构不能省略 WITHIN GROUP 子句(除非用 DISTINCT 简写):
-
LISTAGG(column, ',')本身不合法,必须跟WITHIN GROUP (ORDER BY ...) - 若不需要排序,可写
WITHIN GROUP (ORDER BY NULL),但注意 Oracle 19c 允许省略该子句仅当使用DISTINCT:如LISTAGG(DISTINCT col, ',') - 返回值默认为
VARCHAR2(4000),超长会报ORA-01489;加ON OVERFLOW TRUNCATE '...' WITH COUNT可控处理 - 示例:
SELECT department_id, LISTAGG(first_name, ', ') WITHIN GROUP (ORDER BY first_name) FROM employees GROUP BY department_id
XMLAGG 是兼容老版本的备选方案
适用于 Oracle 10g–12c 且无法升级或需 CLOB 返回的场景,但写法冗长、易出错:
- 必须嵌套
XMLELEMENT+.extract('//text()').getClobVal()才能去掉 XML 标签 - 不显式排序时结果顺序不确定,强烈建议加上
ORDER BY(在XMLAGG内部) - 分隔符需手动拼在
XMLELEMENT的第二个参数里,例如XMLELEMENT(e, col || ',').extract(...),末尾会多一个逗号,得用RTRIM或正则清理 - 示例:
SELECT department_id, RTRIM(XMLAGG(XMLELEMENT(e, first_name || ',') ORDER BY first_name).extract('//text()').getClobVal(), ',') FROM employees GROUP BY department_id
自定义 WM\_CONCAT 函数仅限紧急兼容遗留代码
重建 WM_CONCAT 是高风险操作,仅建议用于无法修改 SQL 的只读报表系统:
- 必须创建类型
WM_CONCAT_IMPL和对应TYPE BODY,二者缺一不可 - 返回类型为
VARCHAR2(32767),但实际受 session 参数MAX_STRING_SIZE限制(STANDARD模式下仍为 4000) - 不支持
DISTINCT或排序,行为与旧版一致——按输入行序拼接,无控序能力 - 并行查询下依赖
ODCIAggregateMerge实现,若未正确实现会导致结果缺失或重复 - 部署后需显式授权:
GRANT EXECUTE ON WM_CONCAT_IMPL TO PUBLIC,否则其他用户无法调用
去重需求必须用 LISTAGG(DISTINCT …) 或预聚合
WM_CONCAT(DISTINCT ...) 在任何版本都不合法,所谓“去重”只是误传。真实去重只有两种可靠方式:
- Oracle 19c+:直接用
LISTAGG(DISTINCT col, ',') WITHIN GROUP (ORDER BY col) - 所有版本通用:先
GROUP BY去重再聚合,例如SELECT dept, LISTAGG(name, ',') WITHIN GROUP (ORDER BY name) FROM (SELECT DISTINCT dept, name FROM t) GROUP BY dept - 切勿用
REGEXP_REPLACE对LISTAGG结果做二次去重——逻辑错误(如把'12,123'错删成'123')且性能极差
真正麻烦的不是选哪个函数,而是检查所有视图、存储过程、报表 SQL 中是否还藏着 WM_CONCAT。它们可能在某个角落静默失效,直到某次数据库升级后突然崩掉——而那时你已经找不到当初写它的人了。


















