LISTAGG函数:“string连接的结果太长”

我正在使用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错误:ORA-01489:string连接的结果太长

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

有没有办法绕过它,或者有其他的select吗?

由于聚合string可能超过4000字节,因此不能使用LISTAGG函数。 您可以创build一个用户定义的聚合函数来返回CLOB而不是VARCHAR2 。 有一个用户定义的聚合的例子,在Tim从第一个讨论中链接到的原始的askTom讨论中返回一个CLOB

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

这将返回一个clob值,所以对行没有限制。

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

 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限制。

例如,

 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; 

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

 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#' ) 

我可以容忍我的领域连接成多个行,每个行less于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) ; 

listagg最近被ISO SQL标准覆盖(SQL:2016)。 作为其中的一部分,它还得到了一个由Oracle 12cR2支持的on overflow子句。

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

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

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

如果截断发生,可选缺省值为三个(…),并将作为最后一个元素添加。

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

更多关于listaggon overflow子句: http : listagg

pipe理LISTAGG中的溢出

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

示例代码和更多的解释在下面的链接。

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

在某些情况下,意图是获得所有的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忽略NULL的事实。 使用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在子查询中使用GROUP BY x,y也可以达到相同的结果。 ROW_NUMBER的优点是可以使用所有其他聚合函数,如SUM(z)

我们能够使用Oracle LISTAGG在这里解决类似的问题。 有一点,我们正在分组在超过4K的限制,但这很容易解决的第一个数据集合前15个项目进行汇总,其中每个都有一个256K的限制。

更多信息:我们有项目,这些项目有更改订单,而这些项目又有解释。 为什么数据库设置为以256K的限制块来更改文本是未知的,但是它是devise约束之一。 因此,将更改解释提交到表中的应用程序停止在254K并插入,然后获取下一组文本,如果> 254K生成另一行,则等等。因此,我们有一个项目为1:1的更改顺序。 然后我们把这些解释为1:n。 LISTAGG连接所有这些。 我们有RMRKS_SN值,每个备注和/或每个254K的字符为1。

最大的RMRKS_SN被发现是31,所以我做了第一个数据集拉SN 0到15,第二个数据集16到30,最后一个数据集31到45 – 嘿,让我们计划有人join了很多解释一些变化命令!

在SQL报告中,Tablix绑定到第一个数据集。 为了得到其他数据,下面是expression式:

= First(Fields!NON_STD_TXT.Value,“DataSet_EXPLAN”)&First(Fields!NON_STD_TXT.Value,“ds_EXPLAN_SN_16_TO_30”)&First(Fields!NON_STD_TXT.Value,“ds_EXPLAN_SN_31_TO_45”)

对于我们来说,由于安全的限制,我们必须有DB Group的创buildfunction等等。 所以有了一点创造力,我们不必做一个用户聚合或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, ... 

而在其他两个数据集中,只需为FROM中的子查询selectLISTAGG即可:

 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 

… 等等。