【问题标题】:Convert WM_CONCAT to Listagg将 WM_CONCAT 转换为 Listagg
【发布时间】:2015-10-19 11:12:07
【问题描述】:

我的 DBA 正在将我的 oracle 数据库从 v10 升级到 v12。 我有一些使用wm_concat 的旧SP,我需要将其更改为listagg。 有问题的代码是这样的:

Select  registration_id,package_set_id,
        REPLACE(REPLACE(WM_CONCAT(REPLACE( (case when ROW_NUMBER() over (partition by product_id,product_detail_set_id,registration_id,product_family_id,application_id,package_Set_id,
               legal_status order by packset_country)=1 then legal_status else null end), ',' , '#')) OVER (PARTITION BY PRODUCT_ID,  PRODUCT_DETAIL_SET_ID,
               REGISTRATION_ID  ,PRODUCT_FAMILY_ID,APPLICATION_ID,PACKAGE_SET_ID   ORDER BY Packset_country  ), ',' , ' | '), '#', ',') as legal_status,

        (REPLACE(REPLACE(WM_CONCAT(REPLACE(ev_code, ',' , '#')) OVER (PARTITION BY PRODUCT_ID,  PRODUCT_DETAIL_SET_ID,
               REGISTRATION_ID  ,PRODUCT_FAMILY_ID,APPLICATION_ID,PACKAGE_SET_ID   ORDER BY ev_code  ), ',' , ' | '), '#', ',')) EV_CODES,

         min(marketed_date) over (PARTITION BY PRODUCT_ID,  PRODUCT_DETAIL_SET_ID,REGISTRATION_ID  ,PRODUCT_FAMILY_ID,APPLICATION_ID,PACKAGE_SET_ID) as marketed_date,

         (REPLACE(REPLACE(WM_CONCAT(REPLACE(Packset_country, ',' , '#')) OVER (PARTITION BY PRODUCT_ID,  PRODUCT_DETAIL_SET_ID, REGISTRATION_ID ,PRODUCT_FAMILY_ID,
                APPLICATION_ID,PACKAGE_SET_ID   ORDER BY Packset_country, reg_packset_country_id ), ',' , ' | '), '#', ',')) REGISTRATION_PACKSET_COUNTRIES,
         ROW_NUMBER() OVER (PARTITION BY PRODUCT_ID,  PRODUCT_DETAIL_SET_ID,REGISTRATION_ID  ,PRODUCT_FAMILY_ID,APPLICATION_ID,PACKAGE_SET_ID  
                ORDER BY Packset_country desc ,reg_packset_country_id)  ROW_NUM,     
         REPLACE(REPLACE(WM_CONCAT(REPLACE( (case when currently_marketed_in_country='Y' then packset_country end), ',' , '#')) OVER (PARTITION BY PRODUCT_ID,  PRODUCT_DETAIL_SET_ID,
                REGISTRATION_ID  ,PRODUCT_FAMILY_ID,APPLICATION_ID,PACKAGE_SET_ID  ORDER BY packset_country ,currently_marketed_in_country,reg_packset_country_id ), ',' , ' | '), '#', ',') as CURRENTLY_MARKETED_COUNTRIES
from radw_dwh.dw202_fact_reg_pack_countries

预期结果是:

我尝试更改它,但是当我尝试在“LISTAGG”一侧使用“ROW_NUMBER()”时出现问题。

我该如何解决这个问题?

【问题讨论】:

    标签: sql oracle listagg wm-concat


    【解决方案1】:

    LISTAGG的基本语法是:

    LISTAGG(col_name_to_be_aggregated, ',') WITHIN GROUP (ORDER BY col)
    

    在您的情况下,由于您有一个子查询作为结果集为WM_CONCAT,您可以在 LISTAGG 中使用相同的子查询代替 col_name_to_be_aggregated

    我认为您也可以摆脱所有 REPLACE 功能,因为 LISTAGG 可以接受您选择的 delimiter

    试试,

    LISTAGG
    (
      CASE
      WHEN ROW_NUMBER() OVER (PARTITION BY product_id,
                                           product_detail_set_id,
                                           registration_id,
                                           product_family_id,
                                           application_id,
                                           package_Set_id, 
                                           legal_status 
                                           order by packset_country)=1 THEN
        legal_status
      ELSE
        NULL
      END), ',') WITHIN GROUP (ORDER BY required_col)
    

    另外,我想解释一下为什么你需要在 12c 迁移到 LISTAGG。由于 t 已从最新的 12c 版本中删除。因此,任何一直依赖 WM_CONCAT 功能的应用程序在升级到 12c 后将无法运行。阅读Why not use WM_CONCAT function in Oracle?

    对于 11g 之前的第 2 版,您不能使用 LISTAGG。字符串聚合技术有很多,看看我的回答here

    更多关于Oracle String Aggregation Techniques的详情

    【讨论】:

    • 它仍然抛出异常:ORA-30483
    • @AlanMil 对于我看不到的东西我无能为力。此外,如果没有 create 和 insert 语句,就不可能提供进一步的帮助。没有人可以运行您的代码,因为没有人拥有表格和数据。您应该提供一个测试用例,即创建表语句和插入语句作为示例数据。
    • 创建表 DW202_FACT_REG_PACK_COUNTRIES(reg_packset_country_id NUMBER(10)、product_family_id NUMBER(10)、application_id NUMBER(10)、packset_country VARCHAR2(500)、product_id NUMBER(10)、product_detail_set_id NUMBER(10)、registration_id NUMBER (10), package_set_id NUMBER(10), legal_status VARCHAR2(500) );
    • INSERT INTO radw_dwh.dw202_fact_reg_pack_countries(reg_packset_country_id,product_family_id,APPLICATION_ID,packset_country,PRODUCT_ID,product_detail_set_id,registration_id,package_set_id,legal_status)VALUES(10267249,1004887,1004969, '丹麦',3763839,4014072,25351829, 9614690,'')值(10366981,1211763,1221071,'立陶宛',3762066,40264448,25352879,9379580,'')值(11379332,515880,724392,'德国',3755922,4072209,4909517,10695769,处方-Only') VALUES (23733439,3367178, 10512281,'Argentina',3766320,23442608,10512310, 23443326,'Prescription-Only');
    • @AlanMil 好的。现在,发布预期的输出。请编辑您的问题并添加它。
    【解决方案2】:

    作为代码中的示例 odciaggregate 接口:

        create or replace type string_agg_type as object ( total varchar2(4000), 
    static function ODCIAggregateInitialize(sctx IN OUT string_agg_type ) return number,
     member function ODCIAggregateIterate(self IN OUT string_agg_type , value IN varchar2 ) return number, 
    member function ODCIAggregateTerminate(self IN string_agg_type, returnValue OUT varchar2, flags IN number) return number,
     member function ODCIAggregateMerge(self IN OUT string_agg_type, ctx2 IN string_agg_type) return number ); 
    / 
    create or replace type body string_agg_type is static function odciaggregateinitialize(sctx IN OUT string_agg_type) return number is begin sctx := string_agg_type(null); return odciconst.success; end; 
    member function odciaggregateiterate(self IN OUT string_agg_type, value IN varchar2) return number is begin self.total := self.total || ',' || value; return odciconst.success; end; 
    member function odciaggregateterminate(self IN string_agg_type, returnvalue OUT varchar2, flags IN number) return number is begin returnvalue := ltrim(self.total, ','); return odciconst.success; end; 
    member function odciaggregatemerge(self IN OUT string_agg_type, ctx2 IN string_agg_type) return number is begin self.total := self.total || ctx2.total; return odciconst.success; end; 
    end;
    / 
    CREATE or replace FUNCTION stragg(input varchar2) RETURN varchar2 PARALLEL_ENABLE AGGREGATE USING string_agg_type; 
    /
    
    
    with t as ( select 'a1' val from dual union all select 'b2' val from dual ) select stragg(val) as val from t; val --------------------------- a1,b2
    

    【讨论】:

      【解决方案3】:

      采用以下方法的动机

      我们在使用wm_concat(已知错误)或wm_concat-to-list_agg-conversion-for-12c-use 时遇到了一些问题。

      (更不用说在其他解决方案可能非常难以阅读的许多场景中使用 wm_concat 的紧凑/声明式用法。)

      (有时不得不使用xmlagg,因为> 4000 chars/clob 问题或regexp_replace 解决方法wm_concat( distinct ... ) 仿真),例如like here.

      策略

      所以最后在我看来,一个相当不错的策略(可能会因您的环境/需求/策略而有所不同)是,

      1. 创建两个函数
        • create function wm_concat_32767(...(在 varchar(32767) 上工作,这可能从 Oracle 12c 开始)和
          • 取决于您的数据库的MAX_STRING_SIZE,您可能希望将其用于“4000”或其他人
        • create function wm_concat_clob(... 首先在系统模式中的数据库中。
        • 它们应该基于the answer from Dart XKey 提供的代码(可能是从asktom/Tom Kyte 复制的)
      2. create public synonym wm_concat for sys.wm_concat_32767
        • (我通常会让wm_concat 指向可能更快的wm_concat_32767 而不是wm_concat_clob
        • 这使得现有的基于wm_concat(varchar(4000))代码/用法可以轻松重用/迁移
        • 可能已经知道函数的名称,因此很容易上手
        • (关于它的在线文档可能具有误导性的问题是我可以忍受的,因为总体上对我来说好处会超过坏处)
        • 它甚至允许使用具有紧凑声明语法的wm_concat( distinct ... ) 查询
          • 与上面提到的基于listagg/xmlagg/regexp_replace 的解决方法相反
      3. create public synonym wm_concat_clob for sys.wm_concat_clob
      4. 给予wm_concat_*functions适当的公开执行权

      代码

      所以最后我们使用的采用的代码(也许我会随着时间的推移更新上面提到的所有内容):

      wm_concat_32767(...)创作:

      • 根据您的数据库的MAX_STRING_SIZE,您可能希望将其用于“4000”或其他值

      _

      create or replace type  string_agg_type  as object ( 
        total varchar2(32767), 
        static function ODCIAggregateInitialize(sctx IN OUT string_agg_type ) return number,
        member function ODCIAggregateIterate(self IN OUT string_agg_type , value IN varchar2 ) return number, 
        member function ODCIAggregateTerminate(self IN string_agg_type, returnValue OUT varchar2, flags IN number) return number,
        member function ODCIAggregateMerge(self IN OUT string_agg_type, ctx2 IN string_agg_type) return number 
      ); 
      / 
      create or replace type body  string_agg_type  is static function odciaggregateinitialize(sctx IN OUT string_agg_type) return number is begin sctx := string_agg_type(null); return odciconst.success; end; 
        member function odciaggregateiterate(self IN OUT string_agg_type, value IN varchar2) return number is begin self.total := self.total || ',' || value; return odciconst.success; end; 
        member function odciaggregateterminate(self IN string_agg_type, returnvalue OUT varchar2, flags IN number) return number is begin returnvalue := ltrim(self.total, ','); return odciconst.success; end; 
        member function odciaggregatemerge(self IN OUT string_agg_type, ctx2 IN string_agg_type) return number is begin self.total := self.total || ctx2.total; return odciconst.success; end; 
      end;
      / 
      CREATE or replace FUNCTION  wm_concat_32767(input varchar2) RETURN varchar2 PARALLEL_ENABLE AGGREGATE USING string_agg_type; 
      /
      

      wm_concat_clob(...)基于this code from Michel Cadot创建:

      create or replace type  stragg_type4  as object (
        result CLOB,
        static function ODCIAggregateInitialize (sctx IN OUT stragg_type4) return number,
        member function ODCIAggregateIterate (self IN OUT stragg_type4, value IN varchar2) return number,
        member function ODCIAggregateTerminate (self IN stragg_type4, returnValue OUT CLOB, flags IN number) return number,
        member function ODCIAggregateMerge (self IN OUT stragg_type4, ctx2 IN stragg_type4) return number
      );
      /
      
      create or replace type body  stragg_type4  is
      
        static function ODCIAggregateInitialize (sctx IN OUT stragg_type4) return number is begin
          sctx := stragg_type4 (null);
          dbms_lob.createtemporary (lob_loc => sctx.result, cache   => TRUE, dur => dbms_lob.call);
          return ODCIConst.Success;
        end;
      
        member function ODCIAggregateIterate (self IN OUT stragg_type4, value IN varchar2) return number is begin
          self.result := self.result || ',' || value;
          return ODCIConst.Success;
        end;
      
        member function ODCIAggregateTerminate (self IN stragg_type4, returnValue OUT CLOB, flags IN number) return number is begin
          returnValue := ltrim (self.result, ',');
          return ODCIConst.Success;
        end;
      
        member function ODCIAggregateMerge (self IN OUT stragg_type4, ctx2 IN stragg_type4) return number is begin
          self.result := self.result || ctx2.result;
          return ODCIConst.Success;
        end;
      
      end;
      /
      
      sho err
      
      CREATE or replace FUNCTION wm_concat_clob(input varchar2) RETURN CLOB PARALLEL_ENABLE AGGREGATE USING stragg_type4;
      /
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-02-17
        • 1970-01-01
        • 1970-01-01
        • 2020-02-05
        • 1970-01-01
        • 2018-11-05
        • 2019-03-03
        • 1970-01-01
        相关资源
        最近更新 更多