问题描述
我必须在版本为 rp:[Identifier == "urn:mycorp:identifier1"]
=> issue(Type = "http://adatum.com/identifierused",Value = "identifier1");
rp:[Identifier == "urn:mycorp:identifier2"]
=> issue(Type = "http://adatum.com/identifierused",Value = "identifier2");
的 Oracle 数据库服务器上工作。因此我在使用 LISTAGG 函数时不能使用 Oracle Database 12c Release 12.1.0.1.0 - 64bit Production
。为了克服大小超过 4000 字节的列聚合,我想使用提到的建议解决方案 here,这样我就不会收到错误 ON OVERFLOW ...
。
不幸的是,这种方法对我不起作用。即使条件 1=1 永远不应该调用 LISTAGG 函数,我也收到了提到的错误。
这里是查询:
01489. 00000 - "result of string concatenation is too long"
我正在寻找对这种行为的解释以及一种替代方法,这样我就不会收到提到的错误。
解决方法
您可以使用 CLOB
PS E:\works\mslearn-django-views-templates\starter> & C:/Users/xxx/.virtualenvs/starter-Nf07b0Gm/Scripts/Activate.ps1
(starter) PS E:\works\mslearn-django-views-templates\starter>
测试sql
drop function listagg_clob;
drop type listagg_clob_t;
create or replace package list_const_p
is
list_sep varchar2(10) := ',';
end list_const_p;
/
sho err
create type listagg_clob_t as object(
v_liststring varchar2(32767),v_clob clob,v_templob number,static function ODCIAggregateInitialize(
sctx IN OUT listagg_clob_t
) return number,member function ODCIAggregateIterate(
self IN OUT listagg_clob_t,value IN varchar2
) return number,member function ODCIAggregateTerminate(
self IN OUT listagg_clob_t,returnValue OUT clob,flags IN number
) return number,member function ODCIAggregateMerge(
self IN OUT listagg_clob_t,ctx2 IN OUT listagg_clob_t
) return number
);
/
sho err
create or replace type body listagg_clob_t is
static function ODCIAggregateInitialize(sctx IN OUT listagg_clob_t)
return number is
begin
sctx := listagg_clob_t('','',0);
return ODCIConst.Success;
end;
member function ODCIAggregateIterate(
self IN OUT listagg_clob_t,value IN varchar2
) return number is
begin
if nvl(lengthb(v_liststring),0) + nvl(lengthb(value),0) <= 4000 then
self.v_liststring:=self.v_liststring || value || list_const_p.list_sep;
else
if self.v_templob = 0 then
dbms_lob.createtemporary(self.v_clob,true,dbms_lob.call);
self.v_templob := 1;
end if;
dbms_lob.writeappend(self.v_clob,length(self.v_liststring),v_liststring);
self.v_liststring := value || list_const_p.list_sep;
end if;
return ODCIConst.Success;
end;
member function ODCIAggregateTerminate(
self IN OUT listagg_clob_t,flags IN number
) return number is
begin
if self.v_templob != 0 then
dbms_lob.writeappend(self.v_clob,self.v_liststring);
dbms_lob.trim(self.v_clob,dbms_lob.getlength(self.v_clob) - 1);
else
self.v_clob := substr(self.v_liststring,1,length(self.v_liststring) - 1);
end if;
returnValue := self.v_clob;
return ODCIConst.Success;
end;
member function ODCIAggregateMerge(self IN OUT listagg_clob_t,ctx2 IN OUT listagg_clob_t) return number is
begin
if ctx2.v_templob != 0 then
if self.v_templob != 0 then
dbms_lob.append(self.v_clob,ctx2.v_clob);
dbms_lob.freetemporary(ctx2.v_clob);
ctx2.v_templob := 0;
else
self.v_clob := ctx2.v_clob;
self.v_templob := 1;
ctx2.v_clob := '';
ctx2.v_templob := 0;
end if;
end if;
if nvl(lengthb(self.v_liststring),0) + nvl(lengthb(ctx2.v_liststring),0) <= 4000 then
self.v_liststring := self.v_liststring || ctx2.v_liststring;
ctx2.v_liststring := '';
else
if self.v_templob = 0 then
dbms_lob.createtemporary(self.v_clob,self.v_liststring);
dbms_lob.writeappend(self.v_clob,length(ctx2.v_liststring),ctx2.v_liststring);
self.v_liststring := '';
ctx2.v_liststring := '';
end if;
return ODCIConst.Success;
end;
end;
/
sho err
CREATE or replace FUNCTION listagg_clob (input varchar2) RETURN clob
PARALLEL_ENABLE AGGREGATE USING listagg_clob_t;
/
sho err;
;
http://www.itpub.net/forum.php?mod=viewthread&tid=2094642&extra=&highlight=concat&page=1
,这是因为聚合实际上是即时发生的,当 Oracle 评估行组时,但 case
表达式在 select
列表中,因此在结果数据集上进行评估。
您期望的处理应该缓冲所有输入并维护交叉引用(聚合到源行)。或者处理复杂的依赖树,因为在 select
列表中,您可能会在同一表达式中的 group by
列旁边使用聚合结果,这也可能引入循环依赖。
下面是一些日志函数的示例,无论 case
结果如何,它都会在聚合步骤进行评估。并且 case
在聚合之后被评估。
/*Logging table*/
create table t (
/*To have a sequence*/
id number GENERATED ALWAYS as IDENTITY(START with 1 INCREMENT by 1),val number,src varchar2(100)
)
/*Logger to check what is going on here*/
create function f_test(
p_val in number,p_src in varchar2
) return varchar2
as
pragma autonomous_transaction;
begin
insert into t(val,src)
values (p_val,p_src);
commit;
return p_val;
end;
/
/*Log aggregation function invocation and case/where evaluation*/
with a as (
select
level as l,mod(level,3) as grp
from dual
connect by level < 7
)
select
grp,case
when f_test(grp,'CASE CONDITION') > 0
then f_test(grp,'CASE RESULT')
else max(f_test(grp,'AGG'))
end as res_agg
from a
where f_test(grp,'WHERE') = grp
group by grp
GRP | RES_AGG --: | :------ 2 | 2 0 | 0 1 | 1
select *
from t
order by id
ID | VAL | SRC -: | --: | :------------- 1 | 1 | WHERE 2 | 1 | AGG 3 | 2 | WHERE 4 | 2 | AGG 5 | 0 | WHERE 6 | 0 | AGG 7 | 1 | WHERE 8 | 1 | AGG 9 | 2 | WHERE 10 | 2 | AGG 11 | 0 | WHERE 12 | 0 | AGG 13 | 2 | CASE CONDITION 14 | 2 | CASE RESULT 15 | 0 | CASE CONDITION 16 | 1 | CASE CONDITION 17 | 1 | CASE RESULT
dbfiddle here
但是,您可以解决此问题,因为在 Oracle 12c+ 中您可以在 with
子句中声明局部函数。通过这种方式,您可以将数据聚合为集合类型,然后在 PL/SQL 中连接它。或者作为一种通用且更快的方法:如另一个答案中所述,为 clob
数据类型定义自定义 listagg。
with function f_listagg_clob(
p_vc2_tab in sys.odcivarchar2list,p_sep in varchar2 default ','
)
return clob
as
r_clob clob;
begin
for i in 1..p_vc2_tab.count loop
r_clob := r_clob || case when i > 1 then p_sep end || p_vc2_tab(i);
end loop;
return r_clob;
end;
a as (
select
dbms_random.string('X',100) as str
from dual
connect by level < 1000
)
select length(f_listagg_clob(cast(collect(str) as sys.odcivarchar2list))) as result_len
from a
| RESULT_LEN | | ---------: | | 101896 |
dbfiddle here