【问题标题】:oracle plsql aggregate error: ORA-06502: character string buffer too smalloracle plsql 聚合错误:ORA-06502:字符串缓冲区太小
【发布时间】:2012-05-23 02:23:25
【问题描述】:

我编写了一个简单的聚合函数,它应该通过聚合生成的 csv 字符串中的不同值来处理包含 csv 文本的列。当它应该返回结果时,当它以 ORA-06502 错误结束时,这些函数似乎一直工作到最后。

代码如下:

输入定义:

create or replace type array_union_typ as object (

union_agg nvarchar2(1000),

static function ODCIAggregateInitialize(sctx  in out array_union_typ)
                return number,

member function ODCIAggregateIterate   (self  in out array_union_typ,
                                        value in nvarchar2)
                return number,

member function ODCIAggregateTerminate (self         in array_union_typ,
                                        return_value out nvarchar2,
                                        flags        in number)
                return number,

member function ODCIAggregateMerge(self in out array_union_typ,
                                   ctx2 in array_union_typ)
                return number,

static function agg_union(arg1 in nvarchar2,
                          arg2 in nvarchar2)
                return nvarchar2
);

身体:

create or replace type body array_union_typ  is

static function ODCIAggregateInitialize(sctx in out array_union_typ
                                     ) return number is
begin
    sctx := array_union_typ(null);
    return ODCIConst.Success;
end;

member function ODCIAggregateIterate(self in out array_union_typ,
                                     value in nvarchar2
                                     ) return number is
begin
    union_agg := array_union_typ.agg_union(union_agg, value);
    return ODCIConst.Success;
end;

member function ODCIAggregateTerminate(self in array_union_typ,
                                       return_value out nvarchar2,
                                       flags in number
                                       ) return number is
begin
    dbms_output.put_line('result: '''|| union_agg || ''', length:' || length(union_agg)); --this still prints
    return_value := self.union_agg; -- <-- this is where the error is indicated
    --return_value := 'x'; -- returning this still gives the error.
    return ODCIConst.Success;
end;

member function ODCIAggregateMerge(self in out array_union_typ,
                                   ctx2 in array_union_typ
                                   ) return number is
begin
    union_agg := array_union_typ.agg_union(union_agg, ctx2.union_agg);
    return ODCIConst.Success;
end;

static function agg_union(arg1 in nvarchar2,
                          arg2 in nvarchar2)
                return nvarchar2 is
    result nvarchar2(1000);
    orig nvarchar2(1000);
begin
    dbms_output.enable;
    orig := replace(arg1||','||arg2, chr(0));

    FOR rec IN (SELECT DISTINCT(regexp_substr(orig, '[^,]+', 1, level)) AS a
           FROM dual CONNECT BY regexp_substr(orig, '[^,]+', 1, level) IS NOT NULL ORDER BY a) LOOP
      IF result IS NOT NULL THEN
        result := result || ',' || rec.a;
      ELSE
        result := rec.a;
      END IF;
    END LOOP;
    --dbms_output.put_line('endwith: ''' || result || '''');
    RETURN substr(result,1,1000);
end;
end;

这是一个测试表和数据:

SQL> desc uniontest
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 I                                                  NVARCHAR2(50)

SQL> select * from uniontest;

I
--------------------------------------------------
a
a
b,c
b,d,e

最后,如果我尝试使用聚合函数会发生这种情况:

SQL> select array_union(i) from uniontest;
select array_union(i) from uniontest
*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
ORA-06512: at "M35456.ARRAY_UNION_TYP", line 25


result: 'a,b,c,d,e', length:9

如果我只是在违规行中传递一个像“x”这样的单个字符串,我仍然会得到同样的错误。只有在空结果上它才会消失。我被难住了,没有想法。

感谢您的帮助。

顺便说一句,如果有人知道为什么我的 agg_union 函数参数中添加了 \0 字符,我也很想知道这一点。

【问题讨论】:

  • 这似乎是nvarchar2的问题;如果这些只是更改为varchar2,则此方法有效。我看不到明显的解决方法,因为它看起来是内部的,而不是你正在做的事情。你也许可以用 CLOB 来组合一些东西;或对这些值进行解聚合,然后将不同的值传递给listagg(如果有的话)?

标签: oracle plsql aggregate ora-06502


【解决方案1】:

如果我没记错的话

 SELECT DISTINCT(regexp_substr(orig, '[^,]+', 1, level)) AS a
       FROM dual CONNECT BY regexp_substr(orig, '[^,]+', 1, level) IS NOT NULL ORDER BY a) 

导致无限数量的行,因为CONNECT BY 条件不依赖于行。

由于您循环遍历结果并将字符附加到字符串,因此字符串变大了。

【讨论】:

  • 您可以尝试运行它,如果事实符合我的要求,并且该函数正确返回结果,您将看到它。事实上,在我开始处理这个错误之后,我已经从更传统的字符串搜索/循环方法中改变了这部分。另外,在我发布的测试运行中,您可以看到ODCIAggregateTerminate 中的dbms_output.put_line 在错误之前显示其输出:result: 'a,b,c,d,e', length:9
【解决方案2】:

ODCIaggregate 方法似乎不能很好地与nvarchar2 配合使用;您似乎没有做错任何事情,因为如果将它们全部更改为varchar2,它就可以正常工作。

如果您在 11gR2 上,因此有可用的 listagg 函数 - 但目前没有使用它,因为您需要找出不同的值 - 您可以将它与现有的 regexp_substr 函数结合使用,因此您不需要'不需要你自己的类型/功能:

SELECT REPLACE(LISTAGG(a, ',') WITHIN GROUP (ORDER BY a), chr(0), '')
FROM (
    SELECT DISTINCT(regexp_substr(i, '[^,]+', 1, level)) AS a
    FROM uniontest CONNECT BY regexp_substr(i, '[^,]+', 1, level) IS NOT NULL
    ORDER BY a
);

...根据您的数据给出:

REPLACE(LISTAGG(A,',')WITHINGROUP(ORDERBYA),CHR(0),'')
------------------------------------------------------
a,b,c,d,e

在早期版本中,您可以改用SELECT REPLACE(WM_CONCAT(a), chr(0), '')

(此处的chr(0) 和您问题中的\0 似乎与nvarchar2 有关,但其他人需要详细说明...)

【讨论】:

  • 谢谢,从nvarchar2 更改为varchar2 做到了。同样使用listagg 会使代码更好一些。不是说除了我以外的任何人都会看到它。希望我不必在带有重音字符的字符串上使用它。
  • @JoeDemarco - listagg 版本似乎与仍定义为 nvarchar2 的表格列一起使用,所以我认为它可以处理重音字符 - 不过还没有测试过。
  • 是的,聚合可能会起作用,但您仍然必须返回字符串,此时它会被硬塞到varchar2 中,各种欢闹随之而来。通常会导致快速脱发。
猜你喜欢
  • 2019-09-13
  • 1970-01-01
  • 2013-09-14
  • 2011-03-31
  • 2014-10-08
  • 2011-01-05
  • 2011-11-25
  • 1970-01-01
相关资源
最近更新 更多