将WM_CONCAT转换为Listagg

时间:2015-10-19 11:12:07

标签: sql oracle listagg wm-concat

我的DBA正在将我的oracle db从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

预期结果为:enter image description here

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

我该如何解决这个问题?

3 个答案:

答案 0 :(得分:4)

LISTAGG 的基本语法是:

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

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

我认为您还可以摆脱所有 REPLACE 功能,因为LISTAGG可以接受您选择的分隔符

尝试,

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

的更多详情

答案 1 :(得分:0)

例如代码中的代码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

答案 2 :(得分:0)

以下方法的动机

我们在wm_concat(已知错误)或wm_concat - 到 - list_agg - 转换为12c用途时遇到了一些问题。

(更不用说在许多情况下使用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"或其他人
    • 首先在sys-schema中的数据库中
    • 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))< 11.2g 代码/用法
    • 可能已经知道该功能的名称,因此很容易使用
    • (关于它的在线文档可能会产生误导的问题是我可以忍受的问题,因为这样做的好处一般会超过我的不利之处)
    • 它甚至允许使用紧凑的声明性语法来使用wm_concat( distinct ... )查询
      • ,而不是上面提到的基于listagg / xmlagg / regexp_replace的变通办法
  3. create public synonym wm_concat_clob for sys.wm_concat_clob
  4. wm_concat_*函数
  5. 提供正确的公共执行权

    代码

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

    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; 
    /
    
    基于this code from Michel Cadot创建

    wm_concat_clob(...)

    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;
    /