微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Oracle SQL - 即使 CASE 语句的条件未满,也会调用 LISTAGG

如何解决Oracle SQL - 即使 CASE 语句的条件未满,也会调用 LISTAGG

我必须在版本为 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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。