RIGHT JOIN in Oracle query - oracle

I have large Oracle query, but I would like to have a RIGHT JOIN on the below line of code, I tried to use (+) but does not work:
AND SUBSTR(IBT_LINE.USER_1, 0, INSTR(IBT_LINE.USER_1, '-', +1, 3)-1) =
SUBSTR(WORK_ORDER.USER_1, 0, INSTR(WORK_ORDER.USER_1, '-', +1, 3)-1)
The total query:
FROM
CUSTOMER_ORDER, IBT, IBT_LINE,PART, WORK_ORDER
WHERE
CUSTOMER_ORDER.ID = REPLACE(IBT.ID,'X','C')
AND IBT.ID = IBT_LINE.IBT_ID
AND IBT_LINE.PART_ID=PART.ID
AND SUBSTR(IBT_LINE.USER_1, 0, INSTR(IBT_LINE.USER_1, '-', +1, 3)-1) = SUBSTR(WORK_ORDER.USER_1, 0, INSTR(WORK_ORDER.USER_1, '-', +1, 3)-1)
AND WORK_ORDER.WAREHOUSE_ID ='MEX-04' AND WORK_ORDER.STATUS ='R'

It's impossible to do a right/left join with two tables, and have a mandatory value on a field of that table in the same select.
Your problem is not with sintax (+ or JOIN), yout problem is with your select.
You're trying to get an outer join in WORK_ORDER and IBT_LINE, but you are telling oracle to do a full join with this sentence:
....
AND WORK_ORDER.WAREHOUSE_ID ='MEX-04' AND WORK_ORDER.STATUS ='R'
You can try a subselect in your FROM,
....
FROM
CUSTOMER_ORDER, IBT, IBT_LINE,PART,
(SELECT SUBSTR(WORK_ORDER.USER_1, 0, INSTR(WORK_ORDER.USER_1, '-', +1, 3)-1) user_1_formatted
from WORK_ORDER
where WORK_ORDER.WAREHOUSE_ID ='MEX-04'
AND WORK_ORDER.STATUS ='R') sub_work_order
WHERE
CUSTOMER_ORDER.ID = REPLACE(IBT.ID,'X','C')
AND IBT.ID = IBT_LINE.IBT_ID
AND IBT_LINE.PART_ID=PART.ID
AND SUBSTR(IBT_LINE.USER_1, 0, INSTR(IBT_LINE.USER_1, '-', +1, 3)-1) = sub_work_order.user_1_formatted(+)
or a subselect and then filter your results, as you wish.
select *
from (select ... from
CUSTOMER_ORDER, IBT, IBT_LINE,PART, WORK_ORDER
WHERE
CUSTOMER_ORDER.ID = REPLACE(IBT.ID,'X','C')
AND IBT.ID = IBT_LINE.IBT_ID
AND IBT_LINE.PART_ID=PART.ID
AND SUBSTR(IBT_LINE.USER_1, 0, INSTR(IBT_LINE.USER_1, '-', +1, 3)-1) = SUBSTR(WORK_ORDER.USER_1, 0, INSTR(WORK_ORDER.USER_1, '-', +1, 3)-1) (+))
where (work_order is null or (WORK_ORDER.WAREHOUSE_ID ='MEX-04' AND WORK_ORDER.STATUS ='R'))

Related

Oracle Where Case

I need to create a condition in the where clause according to a boolean.
If true then I select the row when the final values are different from the expected (ConfirmedVolume > ExpectedVolume, etc) if false I select everything.
I took a look on some links but couldn't apply the ideia to my query.
eg.
Conditional WHERE clause with CASE statement in Oracle
Oracle Conditional where clause
Code:
SELECT RouteId,
Id,
RouteStatusId,
Address,
Latitude,
Longitude,
ExpectedQty,
ConfirmedQty,
FinalQty,
ExpectedWeight,
ConfirmedWeight,
FinalWeight,
ExpectedVolume,
ConfirmedVolume,
FinalVolume,
Comm,
MAX(EndTime) - MAX(StartTime),
MAX(StartTime),
MAX(EndTime),
WaitTime,
MAX(CAST(EndTime AS DATE)),
UOM,
BPName
FROM
(
SELECT
{RouteStop}.[RouteId] RouteId,
{RouteStop}.[Id] Id,
{RouteStop}.[RouteStatusId] RouteStatusId,
{SiteAddress}.[Address] Address,
{SiteAddress}.[Latitude] Latitude,
{SiteAddress}.[Longitude] Longitude,
SUM({RouteStop_Product}.[ExpectedQty]) ExpectedQty,
SUM({RouteStop_Product}.[ConfirmedQty]) ConfirmedQty,
SUM({RouteStop_Product}.[FinalQty]) FinalQty,
SUM({RouteStop_Product}.[ExpectedWeight]) ExpectedWeight,
SUM({RouteStop_Product}.[ConfirmedWeight]) ConfirmedWeight,
SUM({RouteStop_Product}.[FinalWeight]) FinalWeight,
SUM({RouteStop_Product}.[ExpectedVolume]) ExpectedVolume,
SUM({RouteStop_Product}.[ConfirmedVolume]) ConfirmedVolume,
SUM({RouteStop_Product}.[FinalVolume]) FinalVolume,
{RouteStop}.[Comment] Comm,
CASE WHEN {GPS}.[EventTypeId] = #RouteStopStarted
THEN {GPS}.[DateTime]
ELSE CAST(NULL AS DATE)
END StartTime,
CASE WHEN {GPS}.[EventTypeId] = #RouteStopCompleted
OR {GPS}.[EventTypeId] = #RouteStopFailed
THEN {GPS}.[DateTime]
ELSE CAST(NULL AS DATE)
END EndTime,
{GPS}.[EventTypeId] EventTypeId,
{RouteStop}.[WaitTime] WaitTime,
{Order}.[UOM] UOM,
{BusinessPartner}.[CardName] BPName
FROM {RouteStop}
LEFT JOIN {RouteStop_Product} ON {RouteStop}.[Id] = {RouteStop_Product}.[RouteStopId]
LEFT JOIN {GPS} ON {RouteStop}.[Id] = {GPS}.[RouteStopID]
LEFT JOIN {SiteAddress} ON {RouteStop}.[SiteAddress] = {SiteAddress}.[Id]
LEFT JOIN {BusinessPartner} ON {SiteAddress}.[CardCode] = {BusinessPartner}.[Id]
LEFT JOIN {Order} ON {RouteStop_Product}.[OrderId] = {Order}.[Id]
LEFT JOIN {SitePlanningArea} ON {SiteAddress}.[Id] = {SitePlanningArea}.[SiteAddressId]
WHERE {RouteStop}.[Id] = #RouteStopId
AND ({SitePlanningArea}.[PlanningAreaId] = #PlanningAreaId OR #PlanningAreaId = 0)
GROUP BY {RouteStop}.[RouteId],
{RouteStop}.[Id],
{RouteStop}.[RouteStatusId],
{SiteAddress}.[Address],
{SiteAddress}.[Latitude],
{SiteAddress}.[Longitude],
{RouteStop}.[Comment],
{GPS}.[EventTypeId],
{GPS}.[DateTime],
{RouteStop}.[WaitTime],
{Order}.[UOM],
{BusinessPartner}.[CardName]
)
GROUP BY RouteId,
Id,
RouteStatusId,
Address,
Latitude,
Longitude,
ExpectedQty,
ConfirmedQty,
FinalQty,
ExpectedWeight,
ConfirmedWeight,
FinalWeight,
ExpectedVolume,
ConfirmedVolume,
FinalVolume,
Comm,
WaitTime,
UOM,
BPName
I'd like to do something like that in where:
WHERE {RouteStop}.[Id] = #RouteStopId
AND ({SitePlanningArea}.[PlanningAreaId] = #PlanningAreaId OR #PlanningAreaId = 0)
AND IF boolean = true
ConfirmedVolume > ExpectedValue
AND ConfirmedQuantity > ExpectedQuantity
Reasoning in pseudo-code, you probably need to edit
IF boolean = true
ConfirmedVolume > ExpectedValue
into something like
( (boolean = true and ConfirmedVolume > ExpectedValue) OR (boolean = false) )

ORA-01427: Subquery returns more than one row

When I execute the query below, I get the message like this: "ORA-01427: Sub-query returns more than one row"
Define REN_RunDate = '20160219'
Define MOP_ADJ_RunDate = '20160219'
Define RID_RunDate = '20160219'
Define Mbr_Err_RunDate = '20160219'
Define Clm_Err_RunDate = '20160219'
Define EECD_RunDate = '20160219'
select t6.Member_ID, (Select 'Y' from MBR_ERR t7 where t7.Member_ID = t6.Member_ID and t7.Rundate = &Mbr_Err_RunDate ) Mbr_Err,
NVL(Claim_Sent_Amt,0) Sent_Claims, Rejected_Claims,Orphan_Claim_Amt,Claims_Accepted, MOP_Adj_Sent Sent_MOP_Adj,Net_Sent,
(Case
When Net_Sent < 45000 then 0
When Net_Sent > 25000 then 20500
Else
Net_Sent - 45000
End
)Net_Sent_RI,
' ' Spacer,
Total_Paid_Claims CMS_Paid_Claims, MOP_Adjustment CM_MOP_Adj, MOP_Adjusted_Paid_claims CM_Net_Claims, Estimated_RI_Payment CM_RI_Payment
from
(
select NVL(t3.Member_ID,t5.Member_ID)Member_ID, t3.Claim_Sent_Amt, NVL(t4.Reject_Claims_Amt,0) Rejected_Claims, NVL( t8.Orphan_Amt,0) Orphan_Claim_Amt,
(t3.Claim_Sent_Amt - NVL(t4.Reject_Claims_Amt,0) - NVL(t8.Orphan_Amt,0)) Claims_Accepted,
NVL(t2.MOP_Adj_Amt,0) MOP_Adj_Sent ,
( (t3.Claim_Sent_Amt - NVL(t4.Reject_Claims_Amt,0)) - NVL(t2.MOP_Adj_Amt,0) - NVL(t8.Orphan_Amt,0) ) Net_Sent,
t5.Member_ID CMS_Mbr_ID,t5.Total_Paid_Claims,t5.MOP_Adjustment, t5.MOP_Adjusted_Paid_Claims, t5.Estimated_RI_Payment
From
(
Select t1.Member_ID, Sum( t1.Paid_Amount) Claim_Sent_Amt
From RENS t1
where t1.rundate = &REN_RunDate
group by t1.Member_ID
) t3
Left Join MOP_ADJ t2
on (t3.Member_ID = t2.Member_ID and t2.rundate = &MOP_ADJ_RunDate)
Left Join
(select Member_ID, sum(Claim_Total_Paid_Amount) Reject_Claims_Amt from CLAIM_ERR
where Rundate = &Claim_Err_RunDate
and Claim_Total_Paid_Amount != 0
Group by member_ID
)t4
on (t4.Member_ID = t3.Member_ID )
Full Outer Join
(
select distinct Member_ID,Total_Paid_Claims,MOP_Adjustment,MOP_Adjusted_Paid_Claims, Estimated_RI_Payment
from RID
where Rundate = &RID_RunDate
and Estimated_RI_Payment != 0
)t5
On(t5.Member_ID = t3.Member_ID)
Left Outer Join
(
select Member_ID, Sum(Claim_Paid_Amount) Orphan_Amt
From EECD
where RunDate = &EECD_RunDate
group by Member_ID
)t8
On(t8.Member_ID = t3.Member_ID)
)t6
order by Member_ID
You have this expression among the select columns (at the top of your code):
(Select 'Y' from MBR_ERR t7 where t7.Member_ID = t6.Member_ID
and t7.Rundate = &Mbr_Err_RunDate ) Mbr_Err
If you want to select the literal 'Y', then just select 'Y' as Mbr_Err. If you want to select either 'Y' or null, depending on whether the the subquery returns exactly one row or zero rows, then write it that way.
I suspect this subquery (or perhaps another one in your code, used in a similar way) returns more than one row - in which case you will get exactly the error you got.

Re-writing Query

Overall Task :- I need to retrieve data from 45 fields in system A and dump that data into a temp table which is then picked up by a unix process which produces an xml data file to be imported into system B.
Specific Question : What would be the best way of retrieving the data to be written into the 45 fields. Majority of the data is independent and can't be retrieved using a single statement. The way i currently retrieve this data is as follows (example below)
My temp tables hold the affected properties ID that i need to extract data for. i.e PROP_LIST_TEMP and ASSOC_PROP_TEMP.
SELECT SUBSTR (pro.pro_propref, 1, 25) UPRN,
(SELECT SUBSTR (adr_building, 1, 100)
FROM addresses, address_usages
WHERE aus_adr_refno = adr_refno
AND aus_aut_far_code = 'PHYSICAL'
AND aus_aut_fao_code = 'PRO'
AND (aus_end_date IS NULL OR aus_end_date > SYSDATE)
AND aus_pro_refno = pro.pro_refno)
BUILDING_NAME,
(SELECT CASE
WHEN (adr_street_number like 'BLOC%'
OR adr_street_number like '%-%'
OR adr_street_number like '%/%')
THEN NULL
ELSE regexp_replace (adr_street_number, '[^[:digit:]]+')
END
FROM addresses, address_usages
WHERE aus_adr_refno = adr_refno
AND aus_aut_far_code = 'PHYSICAL'
AND aus_aut_fao_code = 'PRO'
AND (aus_end_date IS NULL OR aus_end_date > SYSDATE)
AND aus_pro_refno = pro.pro_refno)
STREET_NUMBER,
(SELECT CASE
WHEN (adr_street_number like 'BLOC%'
OR adr_street_number like '%-%'
OR adr_street_number like '%/%')
THEN SUBSTR (adr_street_number, 1, 20)
ELSE REGEXP_REPLACE (adr_street_number, '[^[:alpha:]]+', '')
END
FROM addresses, address_usages
WHERE aus_adr_refno = adr_refno
AND aus_aut_far_code = 'PHYSICAL'
AND aus_aut_fao_code = 'PRO'
AND (aus_end_date IS NULL OR aus_end_date > SYSDATE)
AND aus_pro_refno = pro.pro_refno)
STREET_NUMBER_SUFFIX,
(SELECT SUBSTR (ptv_pty_code, 1, 3)
FROM prop_type_values
WHERE ptv_refno = pro.pro_hou_ptv_refno)
HOUSE_TYPE
FROM properties pro
WHERE pro_refno IN (select * from PIMSS_PROP_LIST_TEMP
UNION
select * from PIMSS_ASSOC_PROP_TEMP)
AND pro.pro_hou_hrv_hot_code IN
(SELECT frv_code
FROM first_ref_values
WHERE frv_frd_domain IN ('ASS_OWN_REF')
AND frv_current_ind = 'Y');
Since the where clauses of the subqueries in the select statement are identical, you could simply pull that out into the where clause, like so:
SELECT SUBSTR (pro.pro_propref, 1, 25) UPRN,
SUBSTR (addr.adr_building, 1, 100) BUILDING_NAME,
CASE WHEN (addr.adr_street_number like 'BLOC%'
OR addr.adr_street_number like '%-%'
OR addr.adr_street_number like '%/%')
THEN NULL
ELSE regexp_replace (addr.adr_street_number, '[^[:digit:]]+')
END STREET_NUMBER,
CASE WHEN (addr.adr_street_number like 'BLOC%'
OR addr.adr_street_number like '%-%'
OR addr.adr_street_number like '%/%')
THEN SUBSTR (addr.adr_street_number, 1, 20)
ELSE REGEXP_REPLACE (addr.adr_street_number, '[^[:alpha:]]+', '')
END STREET_NUMBER_SUFFIX,
(SELECT SUBSTR (ptv_pty_code, 1, 3)
FROM prop_type_values
WHERE ptv_refno = pro.pro_hou_ptv_refno) HOUSE_TYPE
FROM properties pro,
(select adr_building,
adr_street_number
FROM addresses, address_usages
WHERE aus_adr_refno = adr_refno
AND aus_aut_far_code = 'PHYSICAL'
AND aus_aut_fao_code = 'PRO'
AND (aus_end_date IS NULL OR aus_end_date > SYSDATE)) addr
WHERE pro.pro_refno = aus_pro_refno
and pro_refno IN (select * from PIMSS_PROP_LIST_TEMP
UNION
select * from PIMSS_ASSOC_PROP_TEMP)
AND pro.pro_hou_hrv_hot_code IN (SELECT frv_code
FROM first_ref_values
WHERE frv_frd_domain IN ('ASS_OWN_REF')
AND frv_current_ind = 'Y');
You might possibly need an outer join if there's a chance that no rows could be returned from the addr subquery.

Join condition in Oracle query

I have following query which update the coulmn "tmp_activation_date" of rec_tmprecord table with max of actdate according to the conditions mentioned in query.
MERGE INTO rec_tmprecord
USING (SELECT rec.ID, MAX (act.actdate) AS tmpactivation_date
FROM rec_tmprecord rec INNER JOIN tmp_asset asset
ON asset.serial = rec.serialtemp
and upper (replace (asset.prodname, 'Ajink ')) = upper (replace (rec.prodnametemp, 'Ajink '))
and NVL(asset.release,'NF') = NVL(rec.tmprelease ,'NF')
and rec.serialtemp != 'aaa-aaaaaaaa'
LEFT JOIN tmp_acti_hist act
ON asset.tmp_id = act.tmp_row_id
WHERE rec.cust_id = 234567
GROUP BY rec.cust_id,asset.serial,rec.ID) new_values
ON (rec_tmprecord.ID = new_values.ID)
WHEN MATCHED THEN
UPDATE
SET tmp_activation_date = new_values.tmpactivation_date
;
But,When i analyzed the data of table "rec_tmprecord",i found that there are some null or blank values present in the "prodnametemp" column of "rec_tmprecord" table.But,column "prodname" of the table "tmp_asset" does not contain any null or blank values.
So,my join condition will fail at "upper (replace (asset.prodname, 'Ajink ')) = upper (replace (rec.prodnametemp, 'Ajink '))" condition and as a result "tmp_activation_date" of rec_tmprecord table will be updated with null value or blank value.
what my requirement is if "prodnametemp" is having null value and "prodname" contains some value then also "tmpactivation_date" will get calculated on the basis of other conditions mentioned in the query.
Anyhelp on this would be greatly appreciated.
Here is a modified version. I modified the join as follows:
upper (replace (asset.prodname, 'Ajink ')) = NVL(upper (replace (rec.prodnametemp, 'Ajink ')), upper (replace (asset.prodname, 'Ajink ')))
EDIT:
The join condition has been compressed, thanks to Alex Poole's suggestion.
Basically, if is NULL, then that condition would return TRUE, because then asset.prodname = asset.prodname.
MERGE INTO rec_tmprecord
USING (SELECT rec.ID, MAX (act.actdate) AS tmpactivation_date
FROM rec_tmprecord rec INNER JOIN tmp_asset asset
ON asset.serial = rec.serialtemp
and upper (replace (asset.prodname, 'Ajink ')) = upper (replace (NVL (rec.prodnametemp, asset.prodname), 'Ajink '))
and NVL(asset.release,'NF') = NVL(rec.tmprelease ,'NF')
and rec.serialtemp != 'aaa-aaaaaaaa'
LEFT JOIN tmp_acti_hist act
ON asset.tmp_id = act.tmp_row_id
WHERE rec.cust_id = 234567
GROUP BY rec.cust_id,asset.serial,rec.ID) new_values
ON (rec_tmprecord.ID = new_values.ID)
WHEN MATCHED THEN
UPDATE
SET tmp_activation_date = new_values.tmpactivation_date
;

Single-row subquery returns more than one row- update with nested selects and substr

I'm trying to write a query for a table that needs to update certain records based on certain perameters. I have several nested selects and a couple of nested substrings which I imagine are what is making it difficult for me to troubleshoot. I solved a couple of these ORA-01427: single row sub-query returns more than one row problems already, but I can't seem to find this one. Essentially what the script is doing is taking two columns from a record and updating them based another record contained within a different table based on a number of different criteria. Below is the current code I am using:
UPDATE CMC_SBEL_ELIG_ENT p
SET (p.CSPI_ID, p.SBEL_EFF_DT) =
(SELECT co.new_plan, co.ch_dt
FROM sbsb_plan_conv co, cmc_sbel_elig_ent p
WHERE co.ch_dt > p.sbel_eff_dt
and co.ch_dt < current_date
AND co.new_plan <> p.CSPI_ID
AND co.sbsb_ck = p.sbsb_ck
AND p.cspi_id IN co.OLD_PLAN
and p.SBEL_ELIG_TYPE IN ('tm','ce','TM','CE')
)
WHERE (p.cspd_cat IN (
select unique substr(o.old_plan, 1, 1)
from facets_ws.sbsb_plan_conv o
where
substr(o.old_plan, 1, 1) IN (
select substr(y.new_plan, 1, 1)
from sbsb_plan_conv y, cmc_sbel_elig_ent u
where y.sbsb_ck = u.sbsb_ck
AND (p.SBEL_ELIG_TYPE IN ('tm','ce','TM','CE'))
and ((substr(y.new_plan, 1, 1) = 'M'
and substr(y.new_plan, 1, 1) != 'R'
and substr(y.new_plan, 1, 1) != 'D')
or (substr(y.new_plan, 1, 1) = 'R'
and substr(y.new_plan, 1, 1) != 'M'
and substr(y.new_plan, 1, 1) != 'D')
or (substr(y.new_plan, 1, 1) = 'D'
and substr(y.new_plan, 1, 1) != 'R'
and substr(y.new_plan, 1, 1) != 'M'))
and o.sbsb_ck = p.sbsb_ck)
)
);
Now when I run the individual queries on the two select statements, they all return definite unique values. So I'm fairly certain they are not the issue.
try executing the following query :-
SELECT count(co.new_plan)
FROM sbsb_plan_conv co, cmc_sbel_elig_ent p
WHERE co.ch_dt > p.sbel_eff_dt
and co.ch_dt < current_date
AND co.new_plan <> p.CSPI_ID
AND co.sbsb_ck = p.sbsb_ck
AND p.cspi_id IN co.OLD_PLAN
and p.SBEL_ELIG_TYPE IN ('tm','ce','TM','CE')
group by co.new_plan;
If the above returns more than one row or if it returns a count more than one, then you might need to change the query to either do a DISTINCT / MIN / use ROWNUM or some other technique.

Resources