LISTAGG函数:“字符串连接的结果太长”

时间:2012-12-10 04:57:04

标签: sql oracle

我正在使用Oracle SQL开发人员版本3.0.04。我试图使用函数LISTAGG将数据分组在一起..

    CREATE TABLE FINAL_LOG AS
    SELECT SESSION_DT, C_IP, CS_USER_AGENT,
    listagg(WEB_LINK, ' ')
        WITHIN GROUP(ORDER BY C_IP, CS_USER_AGENT) "WEB_LINKS"
        FROM webviews
        GROUP BY C_IP, CS_USER_AGENT, SESSION_DT
        ORDER BY SESSION_DT

但是,我一直收到错误,

SQL Error: ORA-01489: result of string concatenation is too long

我很确定输出可能超过4000,因为这里提到的WEB_LINK是url stem和url查询的连接值。

有没有办法解决它还是有其他选择吗?

12 个答案:

答案 0 :(得分:72)

SELECT RTRIM(XMLAGG(XMLELEMENT(E,colname,',').EXTRACT('//text()') ORDER BY colname).GetClobVal(),',') AS LIST
FROM tablename;

这将返回clob值,因此对行没有限制。

答案 1 :(得分:21)

由于聚合字符串可能超过4000个字节,因此无法使用LISTAGG函数。您可以创建一个返回CLOB而不是VARCHAR2的{​​{3}}。有一个用户定义聚合的示例,它返回Tim在第一次讨论中链接到的user-defined aggregate function中的CLOB

答案 2 :(得分:13)

您超出了4000字节的 SQL限制,也适用于LISTAGG

SQL> SELECT listagg(text, ',') WITHIN GROUP (
  2  ORDER BY NULL)
  3  FROM
  4    (SELECT to_char(to_date(level,'j'), 'jsp') text FROM dual CONNECT BY LEVEL < 250
  5    )
  6  /
SELECT listagg(text, ',') WITHIN GROUP (
*
ERROR at line 1:
ORA-01489: result of string concatenation is too long

作为解决方法,您可以使用 XMLAGG

例如,

SQL> SET LONG 2000000
SQL> SET pagesize 50000
SQL> SELECT rtrim(xmlagg(XMLELEMENT(e,text,',').EXTRACT('//text()')
  2                     ).GetClobVal(),',') very_long_text
  3  FROM
  4    (SELECT to_char(to_date(level,'j'), 'jsp') text FROM dual CONNECT BY LEVEL < 250
  5    )
  6  /

VERY_LONG_TEXT
--------------------------------------------------------------------------------
one,two,three,four,five,six,seven,eight,nine,ten,eleven,twelve,thirteen,fourteen
,fifteen,sixteen,seventeen,eighteen,nineteen,twenty,twenty-one,twenty-two,twenty
-three,twenty-four,twenty-five,twenty-six,twenty-seven,twenty-eight,twenty-nine,
thirty,thirty-one,thirty-two,thirty-three,thirty-four,thirty-five,thirty-six,thi
rty-seven,thirty-eight,thirty-nine,forty,forty-one,forty-two,forty-three,forty-f
our,forty-five,forty-six,forty-seven,forty-eight,forty-nine,fifty,fifty-one,fift
y-two,fifty-three,fifty-four,fifty-five,fifty-six,fifty-seven,fifty-eight,fifty-
nine,sixty,sixty-one,sixty-two,sixty-three,sixty-four,sixty-five,sixty-six,sixty
-seven,sixty-eight,sixty-nine,seventy,seventy-one,seventy-two,seventy-three,seve
nty-four,seventy-five,seventy-six,seventy-seven,seventy-eight,seventy-nine,eight
y,eighty-one,eighty-two,eighty-three,eighty-four,eighty-five,eighty-six,eighty-s
even,eighty-eight,eighty-nine,ninety,ninety-one,ninety-two,ninety-three,ninety-f
our,ninety-five,ninety-six,ninety-seven,ninety-eight,ninety-nine,one hundred,one
 hundred one,one hundred two,one hundred three,one hundred four,one hundred five
,one hundred six,one hundred seven,one hundred eight,one hundred nine,one hundre
d ten,one hundred eleven,one hundred twelve,one hundred thirteen,one hundred fou
rteen,one hundred fifteen,one hundred sixteen,one hundred seventeen,one hundred
eighteen,one hundred nineteen,one hundred twenty,one hundred twenty-one,one hund
red twenty-two,one hundred twenty-three,one hundred twenty-four,one hundred twen
ty-five,one hundred twenty-six,one hundred twenty-seven,one hundred twenty-eight
,one hundred twenty-nine,one hundred thirty,one hundred thirty-one,one hundred t
hirty-two,one hundred thirty-three,one hundred thirty-four,one hundred thirty-fi
ve,one hundred thirty-six,one hundred thirty-seven,one hundred thirty-eight,one
hundred thirty-nine,one hundred forty,one hundred forty-one,one hundred forty-tw
o,one hundred forty-three,one hundred forty-four,one hundred forty-five,one hund
red forty-six,one hundred forty-seven,one hundred forty-eight,one hundred forty-
nine,one hundred fifty,one hundred fifty-one,one hundred fifty-two,one hundred f
ifty-three,one hundred fifty-four,one hundred fifty-five,one hundred fifty-six,o
ne hundred fifty-seven,one hundred fifty-eight,one hundred fifty-nine,one hundre
d sixty,one hundred sixty-one,one hundred sixty-two,one hundred sixty-three,one
hundred sixty-four,one hundred sixty-five,one hundred sixty-six,one hundred sixt
y-seven,one hundred sixty-eight,one hundred sixty-nine,one hundred seventy,one h
undred seventy-one,one hundred seventy-two,one hundred seventy-three,one hundred
 seventy-four,one hundred seventy-five,one hundred seventy-six,one hundred seven
ty-seven,one hundred seventy-eight,one hundred seventy-nine,one hundred eighty,o
ne hundred eighty-one,one hundred eighty-two,one hundred eighty-three,one hundre
d eighty-four,one hundred eighty-five,one hundred eighty-six,one hundred eighty-
seven,one hundred eighty-eight,one hundred eighty-nine,one hundred ninety,one hu
ndred ninety-one,one hundred ninety-two,one hundred ninety-three,one hundred nin
ety-four,one hundred ninety-five,one hundred ninety-six,one hundred ninety-seven
,one hundred ninety-eight,one hundred ninety-nine,two hundred,two hundred one,tw
o hundred two,two hundred three,two hundred four,two hundred five,two hundred si
x,two hundred seven,two hundred eight,two hundred nine,two hundred ten,two hundr
ed eleven,two hundred twelve,two hundred thirteen,two hundred fourteen,two hundr
ed fifteen,two hundred sixteen,two hundred seventeen,two hundred eighteen,two hu
ndred nineteen,two hundred twenty,two hundred twenty-one,two hundred twenty-two,
two hundred twenty-three,two hundred twenty-four,two hundred twenty-five,two hun
dred twenty-six,two hundred twenty-seven,two hundred twenty-eight,two hundred tw
enty-nine,two hundred thirty,two hundred thirty-one,two hundred thirty-two,two h
undred thirty-three,two hundred thirty-four,two hundred thirty-five,two hundred
thirty-six,two hundred thirty-seven,two hundred thirty-eight,two hundred thirty-
nine,two hundred forty,two hundred forty-one,two hundred forty-two,two hundred f
orty-three,two hundred forty-four,two hundred forty-five,two hundred forty-six,t
wo hundred forty-seven,two hundred forty-eight,two hundred forty-nine

如果你想连接多列本身有 4000字节,那么你可以连接每列的XMLAGG输出,以避免4000字节的SQL限制。 / p>

例如,

WITH DATA AS
  ( SELECT 1 id, rpad('a1',4000,'*') col1, rpad('b1',4000,'*') col2 FROM dual
  UNION
  SELECT 2 id, rpad('a2',4000,'*') col1, rpad('b2',4000,'*') col2 FROM dual
  )
SELECT ID,
       rtrim(xmlagg(XMLELEMENT(e,col1,',').EXTRACT('//text()') ).GetClobVal(), ',')
       || 
       rtrim(xmlagg(XMLELEMENT(e,col2,',').EXTRACT('//text()') ).GetClobVal(), ',') 
       AS very_long_text
FROM DATA
GROUP BY ID
ORDER BY ID;

答案 3 :(得分:10)

listagg最近被ISO SQL标准所涵盖(SQL:2016)。作为其中的一部分,它还有一个on overflow子句,Oracle 12cR2支持该子句。

LISTAGG(<expression>, <separator> ON OVERFLOW …)

on overflow子句支持truncate选项(替代默认的on overflow error行为)。

ON OVERFLOW TRUNCATE [<filler>] WITH[OUT] COUNT

可选默认为三个句点(...),如果发生截断,将作为最后一个元素添加。

如果指定了count并且发生了截断,则省略的值的数量将放在括号中并附加到结果中。

有关listagg的{​​{1}}条款的更多信息:http://modern-sql.com/feature/listagg

答案 4 :(得分:6)

12cR2中添加的新功能是ON OVERFLOW的{​​{1}}子句。 包含此子句的查询如下所示:

LISTAGG

以上将输出限制为4000个字符,但不会引发SELECT pid, LISTAGG(Desc, ' ' ON OVERFLOW TRUNCATE ) WITHIN GROUP (ORDER BY seq) AS desc FROM B GROUP BY pid; 错误。

这些是ORA-01489子句的一些附加选项:

  • ON OVERFLOW:这会显示ON OVERFLOW TRUNCATE 'Contd..' 字符串的结尾(默认为'Contd..'
  • ...:这将显示4000个字符 没有任何终止字符串。
  • ON OVERFLOW TRUNCATE '':这将显示总数 终止字符后的结尾处的字符数。 例如: - 'ON OVERFLOW TRUNCATE WITH COUNT'
  • ...(5512):如果您希望ON OVERFLOW ERROR失败 LISTAGG错误(无论如何都是默认值)。

<强> LISTAGG Enhancements in 12c R2

答案 5 :(得分:3)

我可以容忍我的字段连接成多行,每行少于4000个字符限制 - 执行以下操作:

with PRECALC as (select 
                 floor(4000/(max(length(MY_COLUMN)+LENGTH(',')))) as MAX_FIELD_LENGTH
                 from MY_TABLE)
select LISTAGG(MY_COLUMN,',') WITHIN GROUP(ORDER BY floor(rownum/MAX_FIELD_LENGTH), MY_COLUMN)
from MY_TABLE, PRECALC
group by floor(rownum/MAX_FIELD_LENGTH)
;

答案 6 :(得分:2)

添加到已接受的答案。 我遇到了类似的问题,并最终使用返回clob而不是varchar2的用户定义函数。这是我的解决方案:

CREATE OR REPLACE TYPE temp_data FORCE AS OBJECT
(
    temporary_data NVARCHAR2(4000)
)
/

CREATE OR REPLACE TYPE temp_data_table FORCE AS TABLE OF temp_data;
/

CREATE OR REPLACE FUNCTION my_agg_func (p_temp_data_table IN temp_data_table, p_delimiter IN NVARCHAR2)
RETURN CLOB IS
  l_string CLOB;
BEGIN
  FOR i IN p_temp_data_table.FIRST .. p_temp_data_table.LAST LOOP
    IF i != p_temp_data_table.FIRST THEN
      l_string := l_string || p_delimiter;
    END IF;
    l_string := l_string || p_temp_data_table(i).temporary_data;
  END LOOP;
  RETURN l_string;
END my_agg_func;
/

现在,而不是做

LISTAGG(column_to_aggregate, '#any_delimiter#') WITHIN GROUP (ORDER BY column_to_order_by)

我必须这样做

my_agg_func (
    cast(
        collect(
            temp_data(column_to_aggregate)
            order by column_to_order_by
        ) as temp_data_table
    ),
    '#any_delimiter#'
)

答案 7 :(得分:2)

管理LISTAGG中的溢出

我们可以使用Database 12c SQL模式匹配函数MATCH_RECOGNIZE来返回一个不超过限制的值列表。

示例代码和以下链接中的更多说明。

https://blogs.oracle.com/datawarehousing/entry/managing_overflows_in_listagg

答案 8 :(得分:0)

我们使用Oracle LISTAGG解决了类似的问题。有一点,我们分组的内容超过了4K限制,但这很容易通过让第一个数据集采集前15个项目进行聚合来解决,每个项目都有256K的限制。

更多信息:我们有项目,其中包含变更单,而后者又有解释。为什么数据库被设置为以256K限制的块来获取更改文本尚不清楚,但它是设计约束之一。因此,将更改解释提供到表中的应用程序停止在254K并插入,然后获取下一组文本,如果&gt; 254K生成另一行等。所以我们有一个改变订单的项目,1:1。然后我们将这些作为1:n进行解释。 LISTAGG连接所有这些。我们有RMRKS_SN值,每个备注1和/或每个254K字符。

最大的RMRKS_SN被发现是31,所以我做了第一个数据集,将SN 0拉到15,第二个数据集16到30,最后一个数据集31到45 - 嘿,让我们计划添加某人对一些变更单有很多解释!

在SQL报告中,Tablix与第一个数据集绑定。要获取其他数据,请参见表达式:

= First(Fields!NON_STD_TXT.Value,&#34; DataSet_EXPLAN&#34;)&amp;首先(字段!NON_STD_TXT.Value,&#34; ds_EXPLAN_SN_16_TO_30&#34;)&amp;首先(字段!NON_STD_TXT.Value,&#34; ds_EXPLAN_SN_31_TO_45&#34;)

对于我们来说,由于安全限制,我们必须让DB Group创建函数等。因此,通过一点创造力,我们不必进行用户聚合或UDF。

如果您的应用程序有某种SN要聚合,则此方法应该有效。我不知道等效的TSQL是什么 - 我们很幸运能够在这份报告中与Oracle打交道,而LISTAGG就是天赐之物。

代码是:

SELECT
LT.C_O_NBR AS LT_CO_NUM,
RT.C_O_NBR AS RT_CO_NUM,
LT.STD_LN_ITM_NBR, 
RT.NON_STD_LN_ITM_NBR,
RT.NON_STD_PRJ_NBR, 
LT.STD_PRJ_NBR, 
NVL(LT.PRPSL_LN_NBR, RT.PRPSL_LN_NBR) AS PRPSL_LN_NBR,
LT.STD_CO_EXPL_TXT AS STD_TXT,
LT.STD_CO_EXPLN_T, 
LT.STD_CO_EXPL_SN, 
RT.NON_STD_CO_EXPLN_T,
LISTAGG(RT.RMRKS_TXT_FLD, '') 
    WITHIN GROUP(ORDER BY RT.RMRKS_SN) AS NON_STD_TXT

FROM ...

    WHERE RT.RMRKS_SN BETWEEN 0 AND 15

GROUP BY 
    LT.C_O_NBR,
    RT.C_O_NBR,
    ...

在其他2个数据集中,只为FROM中的子查询选择LISTAGG:

SELECT
LISTAGG(RT.RMRKS_TXT_FLD, '') 
    WITHIN GROUP(ORDER BY RT.RMRKS_SN) AS NON_STD_TXT

FROM ...

WHERE RT.RMRKS_SN BETWEEN 31 AND 45

...

......等等。

答案 9 :(得分:0)

在某些情况下,目的是获取所有 DISTINCT LISTAGG 键,并且溢出是由LISTAGG连接所有键引起的。

这是一个小例子

create table tab as
select 
  trunc(rownum/10) x,
  'GRP'||to_char(mod(rownum,4)) y,
  mod(rownum,10) z
 from dual connect by level < 100;


select  
 x,
 LISTAGG(y, '; ') WITHIN GROUP (ORDER BY y) y_lst
from tab
group by x;


        X Y_LST                                                            
---------- ------------------------------------------------------------------
         0 GRP0; GRP0; GRP1; GRP1; GRP1; GRP2; GRP2; GRP3; GRP3               
         1 GRP0; GRP0; GRP1; GRP1; GRP2; GRP2; GRP2; GRP3; GRP3; GRP3         
         2 GRP0; GRP0; GRP0; GRP1; GRP1; GRP1; GRP2; GRP2; GRP3; GRP3         
         3 GRP0; GRP0; GRP1; GRP1; GRP2; GRP2; GRP2; GRP3; GRP3; GRP3         
         4 GRP0; GRP0; GRP0; GRP1; GRP1; GRP1; GRP2; GRP2; GRP3; GRP3         
         5 GRP0; GRP0; GRP1; GRP1; GRP2; GRP2; GRP2; GRP3; GRP3; GRP3         
         6 GRP0; GRP0; GRP0; GRP1; GRP1; GRP1; GRP2; GRP2; GRP3; GRP3         
         7 GRP0; GRP0; GRP1; GRP1; GRP2; GRP2; GRP2; GRP3; GRP3; GRP3         
         8 GRP0; GRP0; GRP0; GRP1; GRP1; GRP1; GRP2; GRP2; GRP3; GRP3         
         9 GRP0; GRP0; GRP1; GRP1; GRP2; GRP2; GRP2; GRP3; GRP3; GRP3         

如果组很大,重复的键会快速达到允许的最大长度,并获得ORA-01489: result of string concatenation is too long

不幸的是,不支持LISTAGG( DISTINCT y, '; '),但作为一种解决方法,可以使用LISTAGG ignores NULLs.使用ROW_NUMBER的事实,我们只考虑第一个密钥。

with rn as (
select x,y,z,
row_number() over (partition by x,y order by y) rn
from tab
)
select  
 x,
 LISTAGG( case when rn = 1 then y end, '; ') WITHIN GROUP (ORDER BY y) y_lst,
 sum(z) z 
from rn
group by x
order by x;

         X Y_LST                                       Z
---------- ---------------------------------- ----------
         0 GRP0; GRP1; GRP2; GRP3             45 
         1 GRP0; GRP1; GRP2; GRP3             45 
         2 GRP0; GRP1; GRP2; GRP3             45 
         3 GRP0; GRP1; GRP2; GRP3             45 
         4 GRP0; GRP1; GRP2; GRP3             45 
         5 GRP0; GRP1; GRP2; GRP3             45 
         6 GRP0; GRP1; GRP2; GRP3             45 
         7 GRP0; GRP1; GRP2; GRP3             45 
         8 GRP0; GRP1; GRP2; GRP3             45 
         9 GRP0; GRP1; GRP2; GRP3             45

当然,在子查询中使用GROUP BY x,y可以达到相同的结果。 ROW_NUMBER的优点是可以使用SUM(z)所示的所有其他聚合函数。

答案 10 :(得分:0)

使用CLOB和substr短暂使用12c溢出也将起作用

rtrim(dbms_lob.substr(XMLAGG(XMLELEMENT(E,column_name,',')。EXTRACT('// text()')ORDER BY column_name).GetClobVal(),1000,1),',')

答案 11 :(得分:0)

谢谢您的建议。 串联多个字段时,我遇到了同样的问题,但是即使xmlagg也帮不上忙-我仍然得到了ORA-01489。 经过几次尝试,我找到了原因和解决方法:

  1. 原因:我的xmlagg中的一个字段存储大文本;
  2. 解决方案:应用to_clob()函数。

示例:

rtrim(xmlagg(xmlelement(t, t.field1 ||'|'|| 
                           t.field2 ||'|'|| 
                           t.field3 ||'|'|| 
                           to_clob(t.field4),'; ').extract('//text()')).GetClobVal(),',')

希望对大家有帮助。