70

我正在使用 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 query 的串联值。

有什么办法可以绕过它还是有其他选择?

4

12 回答 12

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

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

于 2015-03-12T11:23:24.797 回答
35

由于聚合字符串可能超过 4000 个字节,因此您不能使用该LISTAGG函数。您可能会创建一个用户定义的聚合函数,该函数返回 aCLOB而不是 a VARCHAR2。有一个用户定义的聚合示例,它CLOB原始 askTom 讨论中返回一个 Tim 从第一个讨论链接到该讨论。

于 2012-12-10T05:22:12.507 回答
28

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

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

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

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

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

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

更多关于listagg'son overflow条款: http: //modern-sql.com/feature/listagg

于 2017-10-23T16:39:23.147 回答
21

您超出了同样适用的 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 限制。

例如,

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;
于 2016-02-17T09:22:37.487 回答
21

12cR2 中添加的一个新特性ON OVERFLOWLISTAGG. 包含此子句的查询如下所示:

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错误(无论如何都是默认设置)。

12c R2 中的 LISTAGG 增强功能

于 2018-02-11T13:10:05.687 回答
6

我可以容忍我的字段连接成多行,每行少于 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)
;
于 2016-11-23T01:02:11.010 回答
5

添加到接受的答案。我遇到了类似的问题,最终使用了一个返回 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#'
)
于 2015-07-21T14:49:17.673 回答
4

管理 LISTAGG 中的溢出

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

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

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

于 2016-04-26T08:36:50.780 回答
2

在某些情况下,目的是获取所有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)

于 2016-07-03T10:10:13.507 回答
1

谢谢你的建议。连接多个字段时我遇到了同样的问题,但甚至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(),',')

希望这对任何人都有帮助。

于 2019-06-19T12:45:52.790 回答
0

我们在这里使用 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 与第一个数据集相关联。要获取其他数据,请使用以下表达式:

=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 创建函数等。因此,有了一点创意,我们就不必进行用户聚合或 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

从 ...

WHERE RT.RMRKS_SN BETWEEN 31 AND 45

...

... 等等。

于 2014-08-05T14:23:44.607 回答
-1

使用 CLOB 和 substr 不使用 12c 溢出也可以

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

于 2019-04-05T15:01:04.783 回答