![](/img/trans.png)
[英]Subquery Listagg get errors: result of string concatenation is too long
[英]LISTAGG function: "result of string concatenation is too long"
我正在使用 Oracle SQL 开发者版本 3.0.04。 我试图使用 function 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
我很确定 output 可能超过 4000,因为这里提到的 WEB_LINK 是 url 词干和 url 查询的串联值。
有什么办法可以绕过它 go 还是有其他选择?
SELECT RTRIM(XMLAGG(XMLELEMENT(E,colname,',').EXTRACT('//text()') ORDER BY colname).GetClobVal(),',') AS LIST
FROM tablename;
这将返回clob值,因此行数不受限制。
由于聚合字符串可以长于4000个字节,因此不能使用LISTAGG
函数。 您可能会创建一个用户定义的聚合函数 ,该函数返回CLOB
而不是VARCHAR2
。 有一个用户定义的聚合示例,该示例在原始的AskTom讨论中返回一个CLOB
,Tim从该第一个讨论链接到该讨论。
您超出了适用于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输出,以避免SQL限制为4000字节。
例如,
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;
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
可选的默认值为三个句点(...),如果发生截断,它将作为最后一个元素添加。
如果指定了with count并且发生了截断,则将省略的值的数量放在方括号中并附加到结果中。
有关listagg
的on overflow
子句的更多信息: http : listagg
LISTAGG
的ON OVERFLOW
子句是12cR2中添加的新功能。 包含此子句的查询如下所示:
SELECT pid, LISTAGG(Desc, ' ' ON OVERFLOW TRUNCATE ) WITHIN GROUP (ORDER BY seq) AS desc
FROM B GROUP BY pid;
以上将输出限制为4000个字符,但不会引发ORA-01489
错误。
这些是ON OVERFLOW
子句的一些附加选项:
ON OVERFLOW TRUNCATE 'Contd..'
:这将在字符串的末尾显示'Contd..'
(默认为...
) ON OVERFLOW TRUNCATE ''
:将显示4000个字符而没有任何终止字符串。 ON OVERFLOW TRUNCATE WITH COUNT
:将在结束字符之后的末尾显示字符总数。 例如: ...(5512)
' ON OVERFLOW ERROR
:如果您期望LISTAGG
失败并出现ORA-01489
错误(无论如何都是默认值)。 我可以容忍将我的字段分成多行,每行少于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)
;
加上已接受的答案。 我遇到了类似的问题,最终使用了一个用户定义的函数,该函数返回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#'
)
管理LISTAGG中的溢出
我们可以使用Database 12c SQL模式匹配函数MATCH_RECOGNIZE返回不超过限制的值列表。
示例代码和以下链接中的更多说明。
https://blogs.oracle.com/datawarehousing/entry/managing_overflows_in_listagg
我们能够使用Oracle LISTAGG解决类似的问题。 在某个时候,我们要分组的内容超过了4K的限制,但是很容易解决,方法是让第一个数据集将前15个项目进行汇总,每个项目都有256K的限制。
更多信息:我们有一些项目,这些项目有变更单,这些变更单又有解释。 为什么不将数据库设置为以256K限制的块形式接受更改文本,这一点尚不清楚,但这是设计约束之一。 因此,将变更说明输入表格的应用程序停止在254K并插入,然后获取下一组文本,如果> 254K则生成另一行,依此类推。因此,我们有一个变更单项目,即1:1。 然后我们将其作为1:n进行解释。 LISTAGG将所有这些串联起来。 我们有RMRKS_SN值,每个注释和/或每个254K字符1。
发现最大的RMRKS_SN是31,所以我进行了第一个数据集SN 0到15的提取,第二个数据集16到30的提取,最后一个数据集31到45的提取-嗨,让我们计划有人为一些更改添加很多解释命令!
在SQL报表中,Tablix绑定到第一个数据集。 要获取其他数据,下面是表达式:
=第一个(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”)
对于我们来说,由于安全限制,我们必须让数据库组创建功能等。 因此,有了一点创造力,我们就不必进行用户聚合或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中的子查询选择LISTAGG:
SELECT
LISTAGG(RT.RMRKS_TXT_FLD, '')
WITHIN GROUP(ORDER BY RT.RMRKS_SN) AS NON_STD_TXT
从...
WHERE RT.RMRKS_SN BETWEEN 31 AND 45
...
... 等等。
在某些情况下,目的是获取所有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
可以达到相同的结果。 ROW_NUMBER
的优点是,可以使用SUM(z)
所示的所有其他聚合函数。
谢谢你的建议。 当连接多个字段时,我遇到了同样的问题,但是即使xmlagg
也帮不上忙-我仍然得到了ORA-01489。 经过几次尝试,我找到了原因和解决方法:
xmlagg
的字段之一存储大文本; to_clob()
函数。 例:
rtrim(xmlagg(xmlelement(t, t.field1 ||'|'||
t.field2 ||'|'||
t.field3 ||'|'||
to_clob(t.field4),'; ').extract('//text()')).GetClobVal(),',')
希望对大家有帮助。
要完成聚合并处理 XML / HTML 内容和 Unicode 字符,请使用以下命令:
SELECT uuid, XMLCAST(XMLAGG(XMLELEMENT(E, TO_NCLOB(text),'') ORDER BY uuid) AS NCLOB) AS text
GROUP BY UUID
如果您只需要处理 XML / HTML 而无需担心 Unicode 字符,可以使用
SELECT uuid, XMLCAST(XMLAGG(XMLELEMENT(E, TO_CLOB(text),'') ORDER BY uuid) AS CLOB) AS text
GROUP BY UUID
这种方法的唯一缺点是您只能 select 分组列和您正在聚合的列。 如果要将结果包含在其他列中,则需要将其放置在公用表表达式 (CTE) 中并将其与 Group By 列连接到同一个表中。
我正在使用名为clob_agg
的自定义 function,使用方式如下:
select clob_agg(*detail_column*)
from *table*
group by *group_column*
真实例子:
select length(clob_agg(x||'')) fullList
from (select level as x
from dual
connect by level < 40000)
output 是: 228887
==> output 的长度:)
对于定义 function(在 sqlDeveloper 中使用它,dataGrip 或其他一些编辑器有时无法执行):
CREATE OR REPLACE TYPE t_clob_agg AS OBJECT
(
g_string clob,
STATIC FUNCTION ODCIAggregateInitialize(sctx IN OUT t_clob_agg)
RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateIterate(self IN OUT t_clob_agg,
value IN clob)
RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateTerminate(self IN t_clob_agg,
returnValue OUT clob,
flags IN NUMBER)
RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateMerge(self IN OUT t_clob_agg,
ctx2 IN t_clob_agg)
RETURN NUMBER
);
/
SHOW ERRORS
CREATE OR REPLACE TYPE BODY t_clob_agg IS
STATIC FUNCTION ODCIAggregateInitialize(sctx IN OUT t_clob_agg)
RETURN NUMBER IS
BEGIN
sctx := t_clob_agg(NULL);
RETURN ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateIterate(self IN OUT t_clob_agg,
value IN clob)
RETURN NUMBER IS
BEGIN
-- Concatenate string only when not already existing in the list (=unique)
SELF.g_string := self.g_string || ',' || value;
RETURN ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateTerminate(self IN t_clob_agg,
returnValue OUT clob,
flags IN NUMBER)
RETURN NUMBER IS
BEGIN
returnValue := RTRIM(LTRIM(SELF.g_string, ','), ',');
RETURN ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateMerge(self IN OUT t_clob_agg,
ctx2 IN t_clob_agg)
RETURN NUMBER IS
BEGIN
SELF.g_string := SELF.g_string || ',' || ctx2.g_string;
RETURN ODCIConst.Success;
END;
END;
/
SHOW ERRORS
CREATE OR REPLACE FUNCTION clob_agg(p_input clob)
RETURN clob
PARALLEL_ENABLE AGGREGATE USING t_clob_agg;
/
SHOW ERRORS
缺少使用CLOB和substr的12c溢出功能也可以
rtrim(dbms_lob.substr(XMLAGG(XMLELEMENT(E,column_name,',')。EXTRACT('// text()')ORDER BY column_name).GetClobVal(),1000,1),',')
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.