简体   繁体   中英

PL SQL multiple rows analysis

I have two tables in Oracle, INVOICES and HOLDS, which I'm connecting using the invoice_id. Not all invoices will have holds, but some invoices will have multiple hold events. I want to select just one invoice and decode the release_status field. If there are multiple hold events, I need to analyse whether it's been released or not. If one line has no release code then the whole invoice is still held. Example table:

Invoice_num     |Hold_Reason    |Release_Lookup_code  

10001           |Inv Hold       |Quick release  
10002           |Inv Hold       |Quick release  
10003           |Inv Hold       |(NULL)  
10004           |Inv Hold       |Quick release  
10004           |Inv Hold       |Quick release  
10004           |Amt Hold       |(NULL)  
10005           |Inv Hold       |Variance Corrected  
10005           |Inv Hold       |Quick release  
10006           |  (NULL)       |(NULL)  

The result I want to show:

Invoice num |Hold Reason    |Hold Status 

10001       |Inv Hold       |Released  
10002       |Inv Hold       |Released  
10003       |Inv Hold       |Held  
10004       |Inv Hold       |Held  
10005       |Inv Hold       |Released  
10006       |(NULL)         |Not Held 

Currently I only have this code, which brings back multiple lines:

select
inv.invoice_num,
hold.hold_reason,
(CASE WHEN hold.invoice_id is not null                                              
then (CASE WHEN HOLD.RELEASE_LOOKUP_CODE is null then 'HELD' else 'RELEASED' END)   
else 'NOT HELD' END) Hold_Status
from AP.AP_INVOICES_ALL INV,
AP.AP_HOLDS_ALL HOLD
WHERE inv.invoice_id = hold.invoice_id (+)
order by 1

I thought of counting the number of invoices, but after that I still need to analyse the content of the Release_Lookup_Code field. I'm not looking for full answers, just some suggestions as to how I should proceed.

Something like this. Do the duplication on invoice hold table.

   with AP_HOLDS_ALL(invoice_id, Hold_Reason, Release_Lookup_code) as
           (select 10001, 'Inv Hold', 'Quick release' from dual
            union all
            select 10002, 'Inv Hold', 'Quick release' from dual
            union all
            select 10003, 'Inv Hold', '' from dual
            union all
            select 10004, 'Inv Hold', 'Quick release' from dual
            union all
            select 10004, 'Inv Hold', 'Quick release' from dual
            union all
            select 10004, 'Amt Hold', '' from dual
            union all
            select 10005, 'Inv Hold', 'Variance Corrected' from dual
            union all
            select 10005, 'Inv Hold', 'Quick release' from dual
            union all
            select 10006, '', '' from dual)
       , AP_HOLDS_ALL_deplicated as
           (select s.*
                 , row_number()
                   over(partition by invoice_id
                        order by decode(Release_Lookup_code,  'Quick release', 1,  'Variance Corrected', 2,  3) desc)
                     rn
              from AP_HOLDS_ALL s)
       , AP_INVOICES_ALL(invoice_id) as
           (select 10001 from dual
            union all
            select 10002 from dual
            union all
            select 10003 from dual
            union all
            select 10004 from dual
            union all
            select 10005 from dual
            union all
            select 10006 from dual)
      select --inv.invoice_num,
             inv.invoice_id
           , hold.hold_reason
           , (case
                when hold.invoice_id is not null then
                  (case
                     when HOLD.RELEASE_LOOKUP_CODE is null then 'HELD'
                     else 'RELEASED'
                   end)
                else
                  'NOT HELD'
              end)
               Hold_Status
        from AP.AP_INVOICES_ALL INV, AP.AP_HOLDS_ALL_deplicated HOLD
       where inv.invoice_id = hold.invoice_id(+) and HOLD.rn = 1
    order by 1

Similar kind of idea to @Arkadiusz's answer but using keep ... dense_rank instead of an explicit step to find a row number:

-- CTEs to provide your sample data
with ap_invoices_all (invoice_id) as (
  select 10001 from dual
  union all select 10002 from dual
  union all select 10003 from dual
  union all select 10004 from dual
  union all select 10005 from dual
  union all select 10006 from dual
), 
ap_holds_all (invoice_id, hold_reason, release_lookup_code) as (
  select 10001, 'Inv Hold', , 'Quick release' from dual
  union all select 10002, 'Inv Hold', 'Quick release' from dual
  union all select 10003, 'Inv Hold', null from dual
  union all select 10004, 'Inv Hold', 'Quick release' from dual
  union all select 10004, 'Inv Hold', 'Quick release' from dual
  union all select 10004, 'Amt Hold', null from dual
  union all select 10005, 'Inv Hold', 'Variance Corrected' from dual
  union all select 10005, 'Inv Hold', 'Quick release' from dual
  union all select 10006, null, null from dual
)
-- end of CTEs for sample data
select
  inv.invoice_id,
  max(hold.hold_reason) keep (dense_rank last order by case
      when hold.release_lookup_code is null then 1 else 0
    end) as hold_reason,
  case
    when max(hold.hold_reason) is null then 'NOT HELD'
    when max(hold.release_lookup_code)
      keep (dense_rank last order by case
          when hold.release_lookup_code is null then 1 else 0
        end) is not null then 'RELEASED'
    else 'HELD'
  end as hold_status
from ap_invoices_all inv
left join ap_holds_all hold
on hold.invoice_id = inv.invoice_id
group by inv.invoice_id
order by 1;

which gets:

INVOICE_ID HOLD_REA HOLD_STA
---------- -------- --------
     10001 Inv Hold RELEASED
     10002 Inv Hold RELEASED
     10003 Inv Hold HELD    
     10004 Amt Hold HELD    
     10005 Inv Hold RELEASED
     10006          NOT HELD

The ranking is to give priority to the hold records with null release codes, both to find the appropriate hold reason and to decide if there are any open holds - determining if it it is held or released.

With this version, if you have two open holds for an invoice then you'll only see it reported once, even if the hold reason is different (say if you has both amt and inv holds open for 10004). If you want to see both then you can group by both fields, which would simplify the rest of the query slightly; but you would also see released holds for both reasons too. You could also prioritise which hold reason you see in that scenario instead, if there is a preference.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM