row_number()超过分区

时间:2016-07-07 15:53:45

标签: sql oracle plsql cursor partition

我有现有的PL / SQL代码,其中包含客户端进程的格式化,该进程使用row_number()over partition by来执行多个表上的连接的情况。一切都很好,直到我们注意到如果某个记录上有多个联系人信息,格式化它的设置方式,而不是将所有联系人包括在同一记录中,它将联系人输出为不同的行同一个客户。如何调整光标使其显示为包含所有联系信息的一条记录? 预期:

+-------+-----------+------------+-------------------+-------------+-----------+-------------------+------------+
|  ID   |    NAME   |   CONTACT1 |       EMAIL1      |    PHONE1   |  CONTACT2 |       EMAIL2      |    PHONE2  |
+-------+-----------+------------+-------------------+-------------+-----------+-------------------+------------+
| 50000 | Customer1 | Rodney     |  Rodney@gmail.com |  1112223333 |  Billy    | Billy@hotmail.com | 4445556666 |
+-------+-----------+------------+-------------------+-------------+-----------+-------------------+------------+

而不是:

+-------+------------+-----------+-------------------+-------------+
|  ID   |    NAME    |  CONTACT1 |       EMAIL1      |   PHONE1    |
+-------+------------+-----------+-------------------+-------------+
| 50000 |  Customer1 | Rodney    | Rodney@gmail.com  |  1112223333 |
| 50000 |  Customer1 | Billy     | Billy@hotmail.com | 4445556666  |
+-------+------------+-----------+-------------------+-------------+

代码如下:

cursor c1 is
       select case rn1 when 1 then "TypeOfContract" end "TypeOfContract",
       case rn1 when 1 then "ContractNumber" end "ContractNumber",
       case rn1 when 1 then "ClientName" end "ClientName",
       "AdminName",
       --case rn1 when 1 then "AdminName" end "AdminName",
       --case rn1 when 1 then "TechnicalName" end "TechnicalName",
       "TechnicalName",
       --case rn1 when 1 then "DayToDayName" end "DayToDayName",
       "DayToDayName",
       case rn2 when 1 then "ServiceName" end "ServiceName",
       case rn2 when 1 then "ServiceNumber" end "ServiceNumber",
       "SubserviceName",
       "SubserviceNumber",
       "Map",
       "VolumeOfFilesMessages",
       "VolumeOfPayments",
       "DollarAmountOfPayments"
from (select "TypeOfContract","ContractNumber","ClientName","AdminName","TechnicalName","DayToDayName",
        "ServiceName","ServiceNumber","SubserviceName","SubserviceNumber","Map","VolumeOfFilesMessages","VolumeOfPayments","DollarAmountOfPayments",
        row_number() over (partition by "TypeOfContract","ContractNumber","ClientName","AdminName","TechnicalName","DayToDayName"
        order by "ServiceName","ServiceNumber") rn1,
        row_number() over (partition by "TypeOfContract","ContractNumber","ClientName"/*,"AdminName","TechnicalName","DayToDayName"*/,"ServiceName","ServiceNumber"
        order by null) rn2
          from (
SELECT DISTINCT
case when tctc_cntipcli='C' then 'Host2Host' when tctc_cntipcli='L' then 'File Transfer Services' when tctc_cntipcli='I' then 'Integrated Payables' when tctc_cntipcli='D' then 'Data Exchange'
when tctc_cntipcli='V' then 'Vendor' when tctc_cntipcli='E' then 'External Bank'end as "TypeOfContract" ,
                   tctc_cncclipu as "ContractNumber",
                   regexp_replace(tctc_cndocidc, '[^[:alnum:]'' '']', NULL)  as "ClientName",
                   case when tcct_cncctto = 'A' then (select trim(tcct_cnctname)||trim(tcct_cnemail)||trim(tcct_cnphone) from dual) end as "AdminName"  ,
                  case when tcct_cncctto = 'T' then (select trim(tcct_cnctname)||trim(tcct_cnemail)||trim(tcct_cnphone) from dual) end as "TechnicalName",
                   case when tcct_cncctto = 'D' then (select trim(tcct_cnctname)||trim(tcct_cnemail)||trim(tcct_cnphone) from dual) end as "DayToDayName",
                   tsrv_cndesser as "ServiceName",
                   texe_cnfuncid as "ServiceNumber",
                   tsrs_cnsubsdc as "SubserviceName",
                   texe_cnsubser as "SubserviceNumber",
                   tmap_cndesc   as "Map"
                         from service.kndtctc, service.kndtexe, service.kndtscm, service.kndtsrv, service.kndtsrs, service.kndtmap, service.kndtcct
                          where tctc_cncclipu = texe_cncclipu
                          and texe_cnfuncid = tsrv_cncveser
                          and texe_cnfuncid = tsrs_cncveser
                          and texe_cnsubser = tsrs_cnsubser
                          and texe_cncclipu = tscm_cncontra
                          and tscm_cnmapco = tmap_cnmapco
                          and tscm_cnservic = tsrv_cncveser
                          and tscm_cnsubser = tsrs_cnsubser
                          and texe_cncclipu = tcct_cncclipu
                          and tscm_cncontra = tcct_cncclipu
                          and tctc_cnestado in ('01', '03')
                          and texe_cnestado in ('01', '03')
                          and tsrv_cnestado in ('01', '03')
                          and tsrs_cnestado in ('01', '03')
                          and tscm_cnestado in ('01', '03')
                          and tmap_cnestado in ('01', '03')
                          order by tctc_cncclipu
               ) 
       )
;

感谢社区提供的任何帮助!

2 个答案:

答案 0 :(得分:0)

当您使用PL / SQL时,可以在没有动态SQL的情况下执行此操作。您需要将光标分成两部分。第一个将返回您需要的主要细节。也就是说,此光标将为您需要在输出中看到的每个物理行返回一行。第二个将为每个联系返回一行。

然后,您可以在嵌套循环中连接细节。代码的粗略形状如下。这需要更多的代码,但会得到你想要的效果。

DECLARE

    CURSOR c_main
    IS
    SELECT ...

    CURSOR c_contact
    IS
    SELECT ...

    v_full_line  VARCHAR2(4000);

BEGIN

    FOR r_main IN c_main LOOP

        v_full_line := r_main.id || ' | ' || r_main.name || ' | ';

        FOR r_contact IN c_contact(r_main.id) LOOP
            v_full_line := v_full_line || r_contact.contact || ' | ' || r_contact.email || ' | ' || r_contact.phone || ' | ';
        END LOOP;

        -- Return v_full_line here...
    END LOOP;
END;
/

答案 1 :(得分:0)

函数示例(p_maxcol - 查询中的最大列数。您只需在查询中运行max(count(*)))。

CREATE OR REPLACE FUNCTION get_allitems(p_maxcol number)
  RETURN SYS_REFCURSOR
AS
  my_cursor SYS_REFCURSOR;
  res varchar2(32767);
  -- type in this variable your query.
  query varchar2(32767) := q'[(select  50000 as id ,  'Customer1' as NAME, 'Rodney' as CONTACT  , 'Rodney@gmail.com' as EMAIL,  1112223333 as  PHONE   from dual union all
                              select 50000 ,  'Customer1' , 'Billy'    , 'Billy@hotmail.com' , 4445556666  from dual union all
                              select 60000 ,  'Customer2' , 'Garry'    , 'Garry@hotmail.com' , 1232356666  from dual)]';
BEGIN
  res := q'{select id, EXTRACTVALUE(xml,'/PivotSet/item[1]/column[4]') as NAME}';
  for i in 1..p_maxcol loop
    res := res || q'{,EXTRACTVALUE(xml,'/PivotSet/item[}' || i || q'{]/column[1]') as CONTACT }' ||
                  q'{,EXTRACTVALUE(xml,'/PivotSet/item[}' || i || q'{]/column[2]') as EMAIL }' ||
                  q'{,EXTRACTVALUE(xml,'/PivotSet/item[}' || i || q'{]/column[3]') as PHONE }';
  end loop;
  res := res || 'from (select id, contact_email_phone_xml as xml from ' || query || ' pivot xml (max(NAME) as d for (CONTACT,EMAIL,PHONE) in(any,any,any)))';
  OPEN my_cursor FOR res;
  RETURN my_cursor;
END get_allitems;

用法

select get_allitems(2) from dual

另请注意,Oracle中的最大列数为255。

在静态sql中,你的更多相关例子(带有连接列)是这样的(注意。最大列长度是4000):

with t( ID , NAME  ,  CONTACT , EMAIL , PHONE) as(
select  50000 ,  'Customer1' , 'Rodney'   , 'Rodney@gmail.com' ,  1112223333  from dual union all
select 50000 ,  'Customer1' , 'Billy'    , 'Billy@hotmail.com' , 4445556666  from dual union all
select 60000 ,  'Customer2' , 'Garry'    , 'Garry@hotmail.com' , 1232356666  from dual)
select id, name, listagg(CONTACT || '|' || EMAIL || '|' || PHONE || '|') within group (order by CONTACT) from t
 group by  id, name