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.