oralce函数wm_concat 替代办法

本文探讨了Oracle中wm_concat函数的替代方案,提出了使用自定义类型实现更高效的行列转换,并解决了大表数据处理时的ORA-06502错误。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原帖地址:http://blog.youkuaiyun.com/pengdingxu10/article/details/46438567

oracle11g后 推荐使用listagg函数。


vm_concat

1.该函数不是oracle公开的系统函数,它的用户是wmsys,而不是sys或者system,oracle很有可能在版本升级或者补丁的时候取消或者修改这个函数甚至用户,这种变化oracle是不会公开的。所有可能会由于这个变化而导致异常。

2.大量使用这个函数也会导致临时表空间爆满,这是因为在10.2.0.5中,使用wmsys.wm_concat返回的结果格式是CLOB,CLOB占用的临时表空间只有在连接释放后才会释放,部分通过连接池连接数据库的长连接很有可能导致CLOB占用临时表空间不断累积增大,会导致临时表空间爆满的故障

3.如果是在程序中大量使用这个函数的话会引起enq:TT的锁,可能会导致某些对象被锁。

解决办法:

官方文档给了建议,可以参考该函数自己建立一个函数实现相同的行列转换功能。


CREATE OR REPLACE TYPE en_concat_im AUTHID CURRENT_USER AS OBJECT
(
  CURR_STR VARCHAR2(32767),
  STATIC FUNCTION ODCIAGGREGATEINITIALIZE(SCTX IN OUT en_concat_im)
    RETURN NUMBER,
  MEMBER FUNCTION ODCIAGGREGATEITERATE(SELF IN OUT en_concat_im,
                                       P1   IN VARCHAR2) RETURN NUMBER,
  MEMBER FUNCTION ODCIAGGREGATETERMINATE(SELF        IN en_concat_im,
                                         RETURNVALUE OUT VARCHAR2,
                                         FLAGS       IN NUMBER)
    RETURN NUMBER,
  MEMBER FUNCTION ODCIAGGREGATEMERGE(SELF  IN OUT en_concat_im,
                                     SCTX2 IN en_concat_im) RETURN NUMBER
);


CREATE OR REPLACE TYPE BODY en_concat_im IS
  STATIC FUNCTION ODCIAGGREGATEINITIALIZE(SCTX IN OUT en_concat_im)
    RETURN NUMBER IS
  BEGIN
    SCTX := en_concat_im(NULL);
    RETURN ODCICONST.SUCCESS;
  END;
  MEMBER FUNCTION ODCIAGGREGATEITERATE(SELF IN OUT en_concat_im,
                                       P1   IN VARCHAR2) RETURN NUMBER IS
  BEGIN
    IF (CURR_STR IS NOT NULL) THEN
      CURR_STR := CURR_STR || ',' || P1;
    ELSE
      CURR_STR := P1;
    END IF;
    RETURN ODCICONST.SUCCESS;
  END;
  MEMBER FUNCTION ODCIAGGREGATETERMINATE(SELF        IN en_concat_im,
                                         RETURNVALUE OUT VARCHAR2,
                                         FLAGS       IN NUMBER) RETURN NUMBER IS
  BEGIN
    RETURNVALUE := CURR_STR;
    RETURN ODCICONST.SUCCESS;
  END;
  MEMBER FUNCTION ODCIAGGREGATEMERGE(SELF  IN OUT en_concat_im,
                                     SCTX2 IN en_concat_im) RETURN NUMBER IS
  BEGIN
    IF (SCTX2.CURR_STR IS NOT NULL) THEN
      SELF.CURR_STR := SELF.CURR_STR || ',' || SCTX2.CURR_STR;
    END IF;
    RETURN ODCICONST.SUCCESS;
  END;
END;


create or replace FUNCTION en_concat(P1 VARCHAR2) RETURN VARCHAR2
  AGGREGATE USING en_concat_im;


然后运行测试:select en_concat(username) from dba_users;   ok可以实现行列转换。

但是我在运行的时候出了个问题,就是查询的表数据量比较大的时候会报:Oracle ORA-06502: PL/SQL: 数字或值错误 : 字符串缓冲区太小这个错误

我当时不明白什么原因,后面百度了一下这个原因,看到了一篇文章:http://bbs.youkuaiyun.com/topics/360059765

楼主的问题:

使用wmsys.wm_concat多列合成一列遇到问题
ORA-22813: 操作数值超出系统的限制

往下看了之后对比了一下这个问题的解决办法,原理跟上面一样,也是自定义type来实现,但是有一个不同点的就是,上面的实现我标注的地方,返回的是varchar2类型的

在看看这篇文章里面的实现:


CREATE OR REPLACE TYPE zh_concat_im
AUTHID CURRENT_USER AS OBJECT
(
  CURR_STR clob,
  STATIC FUNCTION ODCIAGGREGATEINITIALIZE(SCTX IN OUT zh_concat_im) RETURN NUMBER,
  MEMBER FUNCTION ODCIAGGREGATEITERATE(SELF IN OUT zh_concat_im,
  P1 IN VARCHAR2) RETURN NUMBER,
  MEMBER FUNCTION ODCIAGGREGATETERMINATE(SELF IN zh_concat_im,
  RETURNVALUE OUT clob,
  FLAGS IN NUMBER)
  RETURN NUMBER,
  MEMBER FUNCTION ODCIAGGREGATEMERGE(SELF IN OUT zh_concat_im,
  SCTX2 IN zh_concat_im) RETURN NUMBER
);
 
CREATE OR REPLACE TYPE BODY zh_concat_im
IS
  STATIC FUNCTION ODCIAGGREGATEINITIALIZE(SCTX IN OUT zh_concat_im)
  RETURN NUMBER
  IS
  BEGIN
  SCTX := zh_concat_im(NULL) ;
  RETURN ODCICONST.SUCCESS;
  END;
  MEMBER FUNCTION ODCIAGGREGATEITERATE(SELF IN OUT zh_concat_im,
 
  P1 IN VARCHAR2)
  RETURN NUMBER
  IS
  BEGIN
  IF(CURR_STR IS NOT NULLTHEN
  CURR_STR := CURR_STR || ',' || P1;
  ELSE
  CURR_STR := P1;
  END IF;
  RETURN ODCICONST.SUCCESS;
  END;
  MEMBER FUNCTION ODCIAGGREGATETERMINATE(SELF IN zh_concat_im,
  RETURNVALUE OUT clob,
  FLAGS IN NUMBER)
  RETURN NUMBER
  IS
  BEGIN
  RETURNVALUE := CURR_STR ;
  RETURN ODCICONST.SUCCESS;
  END;
  MEMBER FUNCTION ODCIAGGREGATEMERGE(SELF IN OUT zh_concat_im,
  SCTX2 IN zh_concat_im)
  RETURN NUMBER
  IS
  BEGIN
  IF(SCTX2.CURR_STR IS NOT NULLTHEN
  SELF.CURR_STR := SELF.CURR_STR || ',' || SCTX2.CURR_STR ;
  END IF;
  RETURN ODCICONST.SUCCESS;
  END;
END;
 
create or replace FUNCTION zh_concat(P1 VARCHAR2)
RETURN clob AGGREGATE USING zh_concat_im ;

这个定义的的clob类型,所有这个函数可以实现,没有出现了Oracle ORA-06502:这个错误,顿时恍然啊。

至于clob这个类型跟varchar2各位就查资料看看了,但是目前我还是有点不明白,这样实现也是用clob这个类型,会不会也会临时表空间增大,目前没有测试过。



<think>好的,我现在需要处理用户关于Oracle数据库中wm_concat函数的问题。用户想了解它的使用说明和替代方案,特别是在11g和12c版本中的情况。首先,我得回忆一下wm_concat的基本信息。 首先,wm_concat是Oracle的一个非公开函数,主要用于将多行数据合并成一个字符串,通常用于分组后的字符串聚合。在旧版本如10g中可能默认可用,但在11g和12c中可能被弃用或移除。用户提到的问题在12c中尤其常见,因为Oracle官方可能已经移除了这个函数,导致原有程序出错。 接下来,替代方案。我记得有几个可能的替代方法。首先是使用Oracle官方推荐的LISTAGG函数,这在11g及更高版本中可用。LISTAGG的功能和wm_concat类似,但语法不同,用户可能需要修改现有代码。例如,将wm_concat(column)改为LISTAGG(column, ',') WITHIN GROUP (ORDER BY column)。不过,用户可能希望减少代码改动量,所以另一个方法是手动重建wm_concat函数,通过创建自定义的聚合函数或包来模拟原有功能。 然后,我需要考虑不同版本的兼容性。比如,在11g中wm_concat可能仍然存在,但已被标记为过时,而在12c中被完全移除。因此,用户可能在升级到12c时遇到问题。这时候,引用提供的资料中提到可以通过创建自定义函数来解决,比如使用PL/SQL包来实现wm_concat的功能,或者使用其他方法如XMLAGG。 另外,用户可能需要具体的步骤来重建wm_concat函数。例如,引用[1]和[2]提到可以创建类型和函数替代。这部分需要确保语法正确,并且有适当的权限。例如,创建TYPE和FUNCTION时可能需要DBA权限,或者用户需要有创建类型的权限。 还有,LISTAGG函数的一个限制是它在处理长字符串时可能会遇到VARCHAR2的长度限制,这时候可以考虑使用CLOB或者其他的方法,比如XMLAGG配合JSON转换来避免截断问题。例如,使用XMLAGG(XMLELEMENT(...))然后提取文本内容,再转换为CLOB类型。 此外,用户可能想知道在哪些情况下应该选择哪种替代方案。比如,如果程序需要最小改动,重建wm_concat可能是更好的选择;如果希望使用官方支持且更高效的函数,那么LISTAGG更适合,尽管需要修改代码。另外,对于更高版本或者不同的数据库系统,比如PostgreSQL,可能有不同的替代方案,如STRING_AGG,但用户的问题主要集中在Oracle上。 还需要考虑性能问题。LISTAGG通常性能较好,但在处理大量数据时可能需要优化。而自定义的wm_concat函数可能在效率上不如原生函数,尤其是在大数据量的情况下。因此,用户需要权衡利弊。 最后,用户的问题可能需要示例代码来说明不同的替代方法,比如如何用LISTAGG替换wm_concat,如何创建自定义函数,以及如何处理可能出现的错误,如权限不足或字符串截断。同时,需要提醒用户注意版本差异,确保解决方案适用于他们正在使用的Oracle版本。</think>### Oracle wm_concat函数使用说明及替代方案 #### 一、wm_concat函数背景 wm_concat是Oracle未公开的聚合函数,用于将多行数据合并为字符串(默认以逗号分隔),在低版本(如10g)中可直接使用,但在11g/12c中被官方弃用或移除[^1][^2]。其语法示例: ```sql SELECT deptno, wm_concat(ename) FROM emp GROUP BY deptno; ``` #### 二、替代方案对比 | 方法 | 版本支持 | 优点 | 缺点 | |--------------------|------------|-----------------------------|------------------------------| | LISTAGG函数 | 11g及以上 | 官方支持,性能高效 | 字符串长度限制(4000字符) | | 自定义重建wm_concat| 所有版本 | 代码零修改 | 需要手动创建,维护成本高 | | XMLAGG方法 | 9i及以上 | 支持CLOB类型 | 语法复杂,性能较低 | #### 三、具体实现方案 1. **LISTAGG函数替代** ```sql SELECT deptno, LISTAGG(ename, ',') WITHIN GROUP (ORDER BY ename) AS employees FROM emp GROUP BY deptno; ``` - 超过4000字符时添加**`ON OVERFLOW TRUNCATE`**或改用CLOB: ```sql SELECT deptno, LISTAGG(ename, ',' ON OVERFLOW TRUNCATE) WITHIN GROUP (ORDER BY ename) FROM emp GROUP BY deptno; ``` 2. **重建wm_concat函数(适用于12c)**[^1][^2] 创建类型和函数: ```sql CREATE TYPE wm_concat_impl AS OBJECT ( str CLOB, STATIC FUNCTION odciaggregateinitialize(ctx IN OUT wm_concat_impl) RETURN NUMBER, MEMBER FUNCTION odciaggregateiterate(self IN OUT wm_concat_impl, value IN VARCHAR2) RETURN NUMBER, MEMBER FUNCTION odciaggregateterminate(self IN wm_concat_impl, returnvalue OUT CLOB, flags IN NUMBER) RETURN NUMBER ); / CREATE OR REPLACE FUNCTION wm_concat(input VARCHAR2) RETURN CLOB PARALLEL_ENABLE AGGREGATE USING wm_concat_impl; / ``` 3. **XMLAGG方法处理长文本** ```sql SELECT deptno, RTRIM(XMLAGG(XMLELEMENT(e, ename || ',')).EXTRACT('//text()'), ',') AS employees FROM emp GROUP BY deptno; ``` #### 四、版本适配建议 - **Oracle 11g**:优先使用LISTAGG,若遇到兼容性问题可暂时启用wm_concat(需检查是否可用) - **Oracle 12c及以上**:必须使用LISTAGG或自定义函数,官方已彻底移除wm_concat[^1] - **跨数据库兼容**:PostgreSQL可使用STRING_AGG,MySQL使用GROUP_CONCAT[^3] 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值