ORACLE JOIN after LEFT JOIN - oracle

I have four tables, three of them joined with a LEFT JOIN to the main file.
SELECT ZADOCO, CADOCO, RDRORN, RDOORN, PDDOCO
FROM PRODDTA.F1755 T2
LEFT JOIN PRODDTA.F90CG503 TCSRMA ON TCSRMA.CAASTYP = 'R' AND TCSRMA.CADOCO = ZADOCO
LEFT JOIN PRODDTA.F40051 TRMAOS ON (TCSRMA.CAKCOO = TRMAOS.RDRKCO AND TCSRMA.CARORN = TRMAOS.RDRORN AND TCSRMA.CADCTO = TRMAOS.RDRCTO)
LEFT JOIN PRODDTA.F4311 ON (TRMAOS.RDOKCO = PDKCOO AND TO_NUMBER(TRIM(TRMAOS.RDOORN)) = PDDOCO AND TRMAOS.RDOCTO = PDDCTO AND TRMAOS.RDPOLN = PDLNID AND (PDLTTR, PDNXTR) NOT IN (('980', '999')))
WHERE ZASTAW = '2' AND ZADOCO IN (172755, 1672095, 1675619)
This query might bring me duplicates, because they are all LEFT.
How can I restrict the results so table F40051 depends on F4311 (i.e. it doesn't show F40051 (RD) if F4311 (PD) doesn't exist), but still show value 1672095? If I apply an INNER JOIN to F4311...
SELECT ZADOCO, CADOCO, RDRORN, RDOORN, PDDOCO
FROM PRODDTA.F1755 T2
LEFT JOIN PRODDTA.F90CG503 TCSRMA ON TCSRMA.CAASTYP = 'R' AND TCSRMA.CADOCO = ZADOCO
LEFT JOIN PRODDTA.F40051 TRMAOS ON (TCSRMA.CAKCOO = TRMAOS.RDRKCO AND TCSRMA.CARORN = TRMAOS.RDRORN AND TCSRMA.CADCTO = TRMAOS.RDRCTO)
JOIN PRODDTA.F4311 ON (TRMAOS.RDOKCO = PDKCOO AND TO_NUMBER(TRIM(TRMAOS.RDOORN)) = PDDOCO AND TRMAOS.RDOCTO = PDDCTO AND TRMAOS.RDPOLN = PDLNID AND (PDLTTR, PDNXTR) NOT IN (('980', '999')))
WHERE ZASTAW = '2' AND ZADOCO IN (172755, 1672095, 1675619)
... I will lose one record.
Adding a subquery might impact me with the performance, as there would be no direct link, but the SQL would need to run the entire SELECT in the subquery first, which will make it very slow.
Any ideas?

I found by chance a solution:
Using left join and inner join in the same query
Refer to Gajus' answer.
Basically we can play with the parenthesis:
SELECT ZADOCO, CADOCO, RDRORN, RDOORN, PDDOCO
FROM PRODDTA.F1755 T2
LEFT JOIN PRODDTA.F90CG503 TCSRMA ON TCSRMA.CAASTYP = 'R' AND TCSRMA.CADOCO = ZADOCO
LEFT JOIN (PRODDTA.F40051 TRMAOS
JOIN PRODDTA.F4311 ON TRMAOS.RDOKCO = PDKCOO AND TO_NUMBER(TRIM(TRMAOS.RDOORN)) = PDDOCO AND TRMAOS.RDOCTO = PDDCTO AND TRMAOS.RDPOLN = PDLNID AND (PDLTTR, PDNXTR) NOT IN (('980', '999'))
LEFT JOIN PRODDTA.F43121 ON PRMATC = '1' AND PDKCOO = PRKCOO AND PDDOCO = PRDOCO AND PDDCTO = PRDCTO AND PDLNID = PRLNID)
ON TCSRMA.CAKCOO = TRMAOS.RDRKCO AND TCSRMA.CARORN = TRMAOS.RDRORN AND TCSRMA.CADCTO = TRMAOS.RDRCTO
WHERE ZASTAW = '2' AND ZADOCO IN (172755, 1672095, 1675619)
Results:

Perform the join in a sub-query:
SELECT ZADOCO,
CADOCO,
RDRORN,
RDOORN,
PDDOCO
FROM PRODDTA.F1755 T2
LEFT JOIN PRODDTA.F90CG503 TCSRMA
ON ( TCSRMA.CAASTYP = 'R'
AND TCSRMA.CADOCO = ZADOCO)
LEFT JOIN (
SELECT * -- Name the columns you want.
FROM PRODDTA.F40051 TRMAOS
JOIN PRODDTA.F4311
ON ( TRMAOS.RDOKCO = PDKCOO
AND TO_NUMBER(TRIM(TRMAOS.RDOORN)) = PDDOCO
AND TRMAOS.RDOCTO = PDDCTO
AND TRMAOS.RDPOLN = PDLNID
AND (PDLTTR, PDNXTR) NOT IN (('980', '999')))
)
ON ( TCSRMA.CAKCOO = TRMAOS.RDRKCO
AND TCSRMA.CARORN = TRMAOS.RDRORN
AND TCSRMA.CADCTO = TRMAOS.RDRCTO)
WHERE ZASTAW = '2'
AND ZADOCO IN (172755, 1672095, 1675619)
or, INNER JOIN and then RIGHT OUTER JOIN:
SELECT ZADOCO, CADOCO, RDRORN, RDOORN, PDDOCO
FROM PRODDTA.F4311
INNER JOIN PRODDTA.F40051 TRMAOS
ON ( TRMAOS.RDOKCO = PDKCOO
AND TO_NUMBER(TRIM(TRMAOS.RDOORN)) = PDDOCO
AND TRMAOS.RDOCTO = PDDCTO
AND TRMAOS.RDPOLN = PDLNID
AND (PDLTTR, PDNXTR) NOT IN (('980', '999')))
RIGHT OUTER JOIN PRODDTA.F90CG503 TCSRMA
ON ( TCSRMA.CAKCOO = TRMAOS.RDRKCO
AND TCSRMA.CARORN = TRMAOS.RDRORN
AND TCSRMA.CADCTO = TRMAOS.RDRCTO)
RIGHT OUTER JOIN PRODDTA.F1755 T2
ON ( TCSRMA.CAASTYP = 'R'
AND TCSRMA.CADOCO = ZADOCO)
WHERE ZASTAW = '2' AND ZADOCO IN (172755, 1672095, 1675619)
db<>fiddle here

Related

Oracle:if left join table return no rows then change to another left join table

I have two query as below and both of them work well. Now I need to combine them and if the first select find rows then no need to run the second select
1.
select f.*, o.org_sn, ttt.name linkman_name, ttt.phone phone_num
from td_archive_feedback f
left join TD_SM_ORG o
on f.recv_org_id = o.org_id
left join (--if this select returns no row , then change.if find matching rows then don't change
select wm_concat(linkman_name) name,
wm_concat(phone_num) phone,
org_id
from (select linkman_name, phone_num, LINK_ORG_ID, org_id
from TD_SM_LINKMAN
where STATE = '2'
and (LINK_ORG_ID = #link_org_id)) t
group by org_id) ttt
on ttt.org_id = o.org_id
--
left join td_sm_dict_item di
on o.org_level = di.item_id
where f.businessid = #businiessid
select f.*, o.org_sn, ttt.name linkman_name, ttt.phone phone_num
from td_archive_feedback f
left join TD_SM_ORG o
on f.recv_org_id = o.org_id
left join (--if the first left join return no rows ,then change to this
select wm_concat(linkman_name) name,
wm_concat(phone_num) phone,
t.org_id
from (
select linkman_name, phone_num, LINK_ORG_ID, org_id
from TD_SM_LINKMAN
where STATE = '2'
and (LINK_ORG_ID is null or LINK_ORG_ID = '')) t
group by t.org_id) ttt
on ttt.org_id = o.org_id
--
left join td_sm_dict_item di
on o.org_level = di.item_id
where f.businessid = #businessid;
Just like I remarked, if
select wm_concat(linkman_name) name,.... where STATE = '2'
and (LINK_ORG_ID = #link_org_id)) t
... returns no rows,
I want to change this to:
select wm_concat(linkman_name) name,.... where STATE = '2' and (LINK_ORG_ID is null or LINK_ORG_ID = '')) t.
Please help me, thanks.
Use Or condition:
select f.*, o.org_sn, ttt.name linkman_name, ttt.phone phone_num
from td_archive_feedback f
left join TD_SM_ORG o
on f.recv_org_id = o.org_id
left join (--if this select returns no row , then change
select wm_concat(linkman_name) name,
wm_concat(phone_num) phone,
org_id
from (select linkman_name, phone_num, LINK_ORG_ID, org_id
from TD_SM_LINKMAN
where STATE = '2'
and (
(LINK_ORG_ID = #link_org_id) or
(LINK_ORG_ID is null or LINK_ORG_ID = '')
) t
group by org_id) ttt
on ttt.org_id = o.org_id
--
left join td_sm_dict_item di
on o.org_level = di.item_id
where f.businessid = #businiessid

How to assign aliases to multiple select statements

My issue is the right join is not working, I am guessing due to aliases used.
I have commented the lines using -- to indicate the lines that most probably are causing the issue. If I run my Cross Join part of the code separately it works fine.
If I add the cross join as show below, and then do a right join with the table final_leg a I am receiving an error saying 'relation "k" does not exist'.
k is the alias I am using in the last select statement to call the CROSS join part of the table.
WITH final_leg AS(
SELECT y.*
FROM
(
SELECT
y.shipment_id,
y.route_id,
max(leg_sequence_id) max_leg_sequence_id
FROM posimorders.sc_execution_eu.o_detailed_routes_v2 y
group by
1,2
) AS x
INNER JOIN posimorders.sc_execution_eu.o_detailed_routes_v2 y
on x.route_id = y.route_id and x.shipment_id = y.shipment_id and y.leg_sequence_id = x.max_leg_sequence_id
),
dest_leg AS(
SELECT y.*
FROM
(
SELECT
y.shipment_id,
y.route_id,
min(leg_sequence_id) max_leg_sequence_id
FROM
posimorders.sc_execution_eu.o_detailed_routes_v2 y
LEFT JOIN warehouse_attributes w -- Joining to add origin country of origin FC
ON w.warehouse_id = y.leg_warehouse_id
group by
1,2
) x
INNER JOIN posimorders.sc_execution_eu.o_detailed_routes_v2 y
on x.route_id = y.route_id and x.shipment_id = y.shipment_id and y.leg_sequence_id = x.max_leg_sequence_id
),
list_legs_ds AS(
SELECT t1.*, t2.* FROM
(SELECT leg_warehouse_id, SUM(pck_count) AS total_packages
FROM posimorders.sc_execution_eu.o_detailed_routes_v2
WHERE trunc(cpt_date) between '2019-06-16' and '2019-06-22'
and leg_sequence_id = 0
and leg_warehouse_id not like 'X%'
and right(leg_warehouse_id,1) in ('1','2','3','4','5','6','7','8','9') --Only SC and not Airports
group by 1
having sum(pck_count) > 50000
) t1
CROSS JOIN
(select distinct leg_warehouse_id AS lm_ds , destination_country_code
from posimorders.sc_execution_eu.o_detailed_routes_v2
where trunc(cpt_date) BETWEEN '2019-06-16' and '2019-06-22'
and leg_ship_method LIKE 'AMZN_%_PRIME'
) t2
)
SELECT
a.route_warehouse_id,
--k.leg_warehouse_id leg_ware,
--k.leg_warehouse_id lm_ds,
from
final_leg a
inner join dest_leg b
on a.shipment_id = b.shipment_id and a.route_id = b.route_id
--RIGHT JOIN list_legs_ds k
--on a.leg_warehouse_id = k.leg_ware -- and a.leg_ship_method = k.last_ds
The issue is most likely because you haven't aliased the columns in your list_legs_ds subquery, meaning you have at least two columns with the same name.
This is a good example of why you shouldn't, in general, use select *... and why you should be explicit in the columns you're selecting. Try the following instead:
WITH final_leg AS
(SELECT y.*
FROM (SELECT y.shipment_id,
y.route_id,
MAX(leg_sequence_id) max_leg_sequence_id
FROM posimorders.sc_execution_eu.o_detailed_routes_v2 y
GROUP BY 1,
2) AS x
INNER JOIN posimorders.sc_execution_eu.o_detailed_routes_v2 y
ON x.route_id = y.route_id
AND x.shipment_id = y.shipment_id
AND y.leg_sequence_id = x.max_leg_sequence_id),
dest_leg AS
(SELECT y.*
FROM (SELECT y.shipment_id,
y.route_id,
MIN(leg_sequence_id) max_leg_sequence_id
FROM posimorders.sc_execution_eu.o_detailed_routes_v2 y
LEFT JOIN warehouse_attributes w -- Joining to add origin country of origin FC
ON w.warehouse_id = y.leg_warehouse_id
GROUP BY 1,
2) x
INNER JOIN posimorders.sc_execution_eu.o_detailed_routes_v2 y
ON x.route_id = y.route_id
AND x.shipment_id = y.shipment_id
AND y.leg_sequence_id = x.max_leg_sequence_id),
list_legs_ds AS
(SELECT t1.leg_warehouse_id AS leg_ware,
t1.total_packages,
t2.leg_warehouse_id AS last_ds,
t2.destination_country_code
FROM (SELECT leg_warehouse_id AS leg_ware,
SUM(pck_count) AS total_packages
FROM posimorders.sc_execution_eu.o_detailed_routes_v2
GROUP BY 1
HAVING SUM(pck_count) > 50000) t1
CROSS JOIN (SELECT DISTINCT leg_warehouse_id AS last_ds,
destination_country_code
FROM posimorders.sc_execution_eu.o_detailed_routes_v2) t2)
SELECT a.route_warehouse_id,
k.leg_ware,
k.last_ds lm_ds
-- should there be something to aggregate here?
FROM final_leg a
INNER JOIN dest_leg b
ON a.shipment_id = b.shipment_id
AND a.route_id = b.route_id
RIGHT JOIN list_legs_ds k
ON a.leg_warehouse_id = k.leg_ware -- and a.leg_ship_method = k.lm_ds
GROUP BY 1,
2,
3;

Oracle Plus (+) Joins to ANSI conversion

I'm in the middle of a warehouse migration from Oracle to SQL Datawarehouse Azure and ran into an issue with this query.
The original query from Oracle - it returns 1872520 rows.
SELECT
*
FROM
STG_REV_APPORTION_CSC_NO t1,
STG_SEP_VL t2,
STG_SEP_VL t3
WHERE
t3.BUSINESS_DATE(+) = t1.BUSINESS_DATE
AND t3.CSC_APP_NO(+) = t1.CSC_APP_NO
AND t3.JOURNEY_NO(+) = t1.JOURNEY_NO
AND t3.PURSE_TXN_CTR(+) = t1.PURSE_TXN_CTR
AND t2.BUSINESS_DATE(+) = t1.BUSINESS_DATE
AND t2.CSC_APP_NO(+) = t1.CSC_APP_NO
AND t2.JOURNEY_NO(+) = t1.JOURNEY_NO
AND
(
t2.TRIP_NO(+) + 1
)
= t1.TRIP_NO
AND
(
t2.MSG_TYPE_CD(+) = 13070
AND t3.MSG_TYPE_CD(+) = 4357
);
Taking clues from documentation, I tried query re-write to ANSI:
SELECT COUNT(*)
FROM STG_REV_APPORTION_CSC_NO t1
RIGHT OUTER JOIN STG_SEP_VL t3 ON t3.BUSINESS_DATE = t1.BUSINESS_DATE
AND t3.CSC_APP_NO = t1.CSC_APP_NO
AND t3.JOURNEY_NO = t1.JOURNEY_NO
AND t3.PURSE_TXN_CTR = t1.PURSE_TXN_CTR
RIGHT OUTER JOIN STG_SEP_VL t2 ON t2.BUSINESS_DATE = t1.BUSINESS_DATE
AND t2.CSC_APP_NO = t1.CSC_APP_NO
AND t2.JOURNEY_NO = t1.JOURNEY_NO
AND (t2.TRIP_NO + 1) = t1.TRIP_NO
WHERE t2.MSG_TYPE_CD = 13070 AND t3.MSG_TYPE_CD = 4357
It returns zero rows. The ANSI version should work on oracle instance - it returns zero rows there too.
I then tried to convert plus join to ANSI using refactor option on toad. I get the following
SELECT *
FROM STG_SEP_VL T2
RIGHT OUTER JOIN STG_REV_APPORTION_CSC_NO T1
ON (T2.BUSINESS_DATE = T1.BUSINESS_DATE)
AND (T2.CSC_APP_NO = T1.CSC_APP_NO)
AND (T2.JOURNEY_NO = T1.JOURNEY_NO)
RIGHT OUTER JOIN STG_SEP_VL T3
ON (T3.PURSE_TXN_CTR = T1.PURSE_TXN_CTR)
AND (T3.BUSINESS_DATE = T1.BUSINESS_DATE)
AND (T3.CSC_APP_NO = T1.CSC_APP_NO)
AND (T3.JOURNEY_NO = T1.JOURNEY_NO)
WHERE ( ( (T2.TRIP_NO /*(+)*/
) + 1) = T1.TRIP_NO)
AND ( ( (T2.MSG_TYPE_CD /*(+)*/
) = 13070) AND ( (T3.MSG_TYPE_CD /*(+)*/
) = 4357));
Now this query should run on Oracle and return the same number of rows before I can run it on SQL Server. But it doesn't - it returns zero rows.
I looked at the explain plan for both of these queries. Here is how (+) join plan looks like:
Here is how ANSI version of this query looks like:
Am I missing something?
Here's what I came up with:
SELECT *
FROM stg_rev_apportion_csc_no t1
LEFT JOIN stg_sep_vl t3
ON t1.business_date = t3.business_date AND
t1.csc_app_no = t3.csc_app_no AND
t1.journey_no = t3.journey_no AND
t1.purse_txn_ctr = t3.purse_txn_no AND
4357 = t3.msg_type_cd
LEFT JOIN stg_sep_vl t2
ON t1.business_date = t2.business_date AND
t1.csc_app_no = t2.csc_app_no AND
t1.journey_no = t2.journey_no AND
t1.trip_no = t2.trip_no + 1 AND
13070 = t2.msg_type_cd;
Tables t2 and t3 are outer joined to t1, so you either list t1 first and do a left join, or list t2 and t3 first and do a right join.
Without sample data it is hard to be sure but I think the where clause is to blame.
Including fields from t2 and t3 in the where clause negates the effect of the outer join, unless you also allow nulls (t2.MSG_TYPE_CD = 13070 OR 2.MSG_TYPE_CD IS NULL). Moving those filters into the join allows non-matching records into the results.
SELECT
COUNT(*)
FROM
STG_REV_APPORTION_CSC_NO t1
RIGHT OUTER JOIN STG_SEP_VL t3 ON t3.BUSINESS_DATE = t1.BUSINESS_DATE
AND t3.CSC_APP_NO = t1.CSC_APP_NO
AND t3.JOURNEY_NO = t1.JOURNEY_NO
AND t3.PURSE_TXN_CTR = t1.PURSE_TXN_CTR
AND t3.MSG_TYPE_CD = 4357
RIGHT OUTER JOIN STG_SEP_VL t2 ON t2.BUSINESS_DATE = t1.BUSINESS_DATE
AND t2.CSC_APP_NO = t1.CSC_APP_NO
AND t2.JOURNEY_NO = t1.JOURNEY_NO
AND (t2.TRIP_NO + 1) = t1.TRIP_NO
AND t2.MSG_TYPE_CD = 13070
;
I'm not 100% convinced this query is correct. I suspect the right outer joins should be replaced with left outer joins. That would return every record from t1 and only those from t2 and t3 that match.
It is difficult to find the exact reason for this mismatch but I think you have interchanged the joining condition for the column PURSE_TXN_CTR in table STG_SEP_VL.
SELECT *
FROM STG_REV_APPORTION_CSC_NO t1
RIGHT
JOIN STG_SEP_VL t2
ON t2.BUSINESS_DATE = t1.BUSINESS_DATE
AND t2.CSC_APP_NO = t1.CSC_APP_NO
AND t2.JOURNEY_NO = t1.JOURNEY_NO
AND ( t2.TRIP_NO + 1 ) = t1.TRIP_NO
RIGHT
JOIN STG_SEP_VL t3
ON t3.BUSINESS_DATE = t1.BUSINESS_DATE
AND t3.CSC_APP_NO = t1.CSC_APP_NO
AND t3.JOURNEY_NO = t1.JOURNEY_NO
AND t3.PURSE_TXN_CTR = t1.PURSE_TXN_CTR
WHERE ( t2.MSG_TYPE_CD = 13070 AND t3.MSG_TYPE_CD = 4357 );

how to do left join in lambda

var item= (from t1 in _dbEntities.PurchaseSales
join t2 in _dbEntities.ItemTypes on t1.ItemTypeID equals t2.ID
where t2.ID.Equals(null)
select t2).ToList();
how to do left join in it
Use DefaultIfEmpty:
var item= (from t1 in _dbEntities.PurchaseSales
join t2 in _dbEntities.ItemTypes on t1.ItemTypeID equals t2.ID into t
from l in t.DefaultIfEmpty()
where l == null
select t1).ToList();

ORA-00979: not a GROUP BY expression while querying a View with a subquery

I have a view as follows - It compiles just fine but when I try to select from it, I get a ORA-00979: not a GROUP BY expression error. If I eliminate the subquery (column 4) from the view, all works fine. Any ideas would be greatly appreciated! Thanks!!
select
l.LAB_GROUP,
l.NAME as LAB,
b.NAME as BENCH,
(select count(distinct s2.SAMPLE_NUMBER)
from SAMPLE s2 inner join TEST t2 on s2.SAMPLE_NUMBER = t2.SAMPLE_NUMBER and t2.STATUS in ('C', 'R') and s2.TEMPLATE <> 'QC_SAMPLE'
inner join LABORATORY_ENTRY le2 on t2.ANALYSIS = le2.ANALYSIS
where s2.LAB_GROUP = l.LAB_GROUP and le2.NAME = l.NAME and t2.X_BENCH = b.NAME and
((select count(t1.TEST_NUMBER)
from TEST t1
where t1.SAMPLE_NUMBER = t2.SAMPLE_NUMBER and t1.ANALYSIS = t2.ANALYSIS and t1.STATUS <> 'R') = 0)) as RFR
from LABORATORY l
inner join LABORATORY_ENTRY le on le.NAME = l.NAME
inner join X_BENCH b on b.NAME = le.X_BENCH
left join (SAMPLE s inner join TEST t on s.SAMPLE_NUMBER = t.SAMPLE_NUMBER and s.STATUS <> 'U'
and s.TEMPLATE <> 'QC_SAMPLE' and t.STATUS in ('I', 'P')) on t.ANALYSIS = le.ANALYSIS and s.LAB_GROUP = l.LAB_GROUP
left join V_LOC_DEPT_FAC ldf on ldf.LOCATION_NUMBER = s.STORAGE_LOC_NO
group by l.LAB_GROUP, l.NAME, b.NAME
If you need to use the group by (which will be the case if you add aggregating functions, but not as the query is currently written) you need to include the subquery in the group by as well. You can add this easiest by adding a SELECT outside your main query and appyling the GROUP BY at that level:
select lab_group, lab, bench, rfr
from
(
select
l.LAB_GROUP as lab_group,
l.NAME as LAB,
b.NAME as BENCH,
(select .....) as RFR
from LABORATORY l
inner join LABORATORY_ENTRY le on le.NAME = l.NAME
inner join X_BENCH b on b.NAME = le.X_BENCH
left join (SAMPLE s inner join TEST t on ...) on
t.ANALYSIS = le.ANALYSIS and
s.LAB_GROUP = l.LAB_GROUP
left join V_LOC_DEPT_FAC ldf on ldf.LOCATION_NUMBER = s.STORAGE_LOC_NO
) x
group by lab_group, lab, bench, rfr
Try removing the...
group by l.LAB_GROUP, l.NAME, b.NAME
As I don't think this is neccesary if you're doing the count within the subquery.
However, for performance reasons I would suggest rewriting your query so that you move the subquery into the FROM section rather than the SELECT one.

Resources