Case based Joins in oracle query - oracle

I have below query which returns all the data from inner query including one more field i.e. ctry from tbl_account table.I have used
LEFT JOIN so that even though a_row_id not matched with row_id from tbl_account so that i would still get all the matched data from inner query.
select b.* ,sac.ctry as country from
(SELECT DISTINCT rec.* ,asset.a_row_id
FROM tbl_record rec LEFT JOIN tbl_asset asset
ON asset.from_sn = rec.to_sn
AND asset.from_name = rec.to_productname
AND asset.from_rel = rec.to_rel
LEFT JOIN tbl_country mas
ON rec.loc = mas.a_loc
WHERE rec.cust_id = 2456 ) b
LEFT JOIN tbl_account sac on b.a_row_id = sac.row_id ;
But,now i need to implement case based join in the above query i.e. when a_row_id is not null then inner join with one extra condition will be used and when a_row_id is null left join will be used.
I have tried using CASE statement as below but it is not working and cost of the query is very high as well and I suppose it is because of CASE statement.The
data in all the tables are in millions.
select b.* ,sac.ctry as country from
(SELECT DISTINCT rec.* ,asset.a_row_id
FROM tbl_record rec LEFT JOIN tbl_asset asset
ON asset.from_sn = rec.to_sn
AND asset.from_name = rec.to_productname
AND asset.from_rel = rec.to_rel
LEFT JOIN tbl_country mas
ON rec.loc = mas.a_loc
WHERE rec.cust_id = 2456 ) b
INNER JOIN tbl_account sac
ON CASE
WHEN b.a_row_id IS NOT NULL AND b.a_row_id = sac.row_id and b.from_cn = SAC.to_cn THEN 1
END = 1
LEFT JOIN tbl_account sac
ON CASE
WHEN b.a_row_id IS NULL AND b.a_row_id = sac.row_id THEN 1
END = 1 ;
Is there any other way that i can implement case based joins condition in above oracle query and at the same time cost of query would be less.Is it possible to use decode in this case ? Any help on this would be greatly appreciated.

This syntax is probably what you are looking for:
select b.*, sac.ctry as country
from b
left join tbl_account sac
on b.a_row_id = sac.row_id or (b.a_row_id is null and sac.row_id is null)
where b.from_cn = sac.to_cn or b.a_row_id is null
SQLFiddle

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

Getting ORA-00918: column ambiguously defined error on 10g but works in 11g

select distinct p.id,
format_name(p.fname, p.lname,'local000000000000001') full_name,
p.username,
p.person_no,
jobinfo.name,
company.name2,
company.name,
dom.name,
per_mgr.username,
a.name
from cmt_person p
inner join fgt_locale loc on loc.id='local000000000000001'
left outer join tpt_company company on p.company_id = company.id
left outer join tpt_ext_job_type jobinfo on p.jobtype_id = jobinfo.id AND jobinfo.locale_id=loc.id
inner join fgt_domain dom on p.split = dom.id
left outer join fgt_gen gen on gen.id1 = p.id and gen.TYPE = 301 and gen.str2 IS NULL
left outer join fgt_ext_admin_managed_lov a on a.id = gen.id2 and a.locale_id = loc.id
left outer join cmt_person per_mgr on per_mgr.id = p.manager_id
inner join fgt_address a on a.owner_id = p.id
where ( p.terminated_on is null or p.terminated_on >= sysdate)
and( (p.type = 100 and p.split in ('domin000000000000007','domin000000000001107','domin000000000001108','domin000000000001109','domin000000000001104','domin000000000001103','domin000000000001106','domin000000000001105','domin000000000000001','domin000000000001102','domin000000000001110'))
or (p.type = 200 and p.split in ('domin000000000000007','domin000000000001107','domin000000000001108','domin000000000001109','domin000000000001104','domin000000000001103','domin000000000001106','domin000000000001105','domin000000000000001','domin000000000001102','domin000000000001110')) )
AND 1=1
AND lower(p.status) = lower('A')
AND lower(a.country) like lower('USA' || '%')
AND p.type in (decode('1','1',100,'2',200,'0',p.type))
AND p.manager_id in ('emplo000000000034578')
ORDER BY 2,3,4,5,6,7,8,9,10
This is the query I have defined table alias for all tables
Do I need column aliases for following columns for 10g?
jobinfo.name ,
company.name2 ,
company.name ,
dom.name
Two tables share the same alias ("a"): fgt_ext_admin_managed_lov and fgt_address. Change it, as that won't work:
SQL> select a.deptno
2 from dept a, emp a;
select a.deptno
*
ERROR at line 1:
ORA-00918: column ambiguously defined
SQL>
Look at below code :-
left outer join fgt_ext_admin_managed_lov a <<<
..
..
inner join fgt_address a <<<<
You have used same alias name for both tables. Change it to any other alias name.

Hive - how to reuse a sub-query in hive with optimal performance

What is the best way to structure/write a query in Hive when I have a complex sub-query that is repeated multiple times throughout the select statement?
I originally created a temporary table for the sub-query which was refreshed before each run. Then I began to use a CTE as part of the original query (discarding the temp table) for readability and noticed degraded performance. This made me curious about which implementation methods are best with respect to performance when needing to reuse sub-queries.
The data I am working with contains upwards of 10 million records. Below is an example of the query I wrote that made use of a CTE.
with temp as (
select
a.id,
x.type,
y.response
from sandbox.tbl_form a
left outer join sandbox.tbl_formStatus b
on a.id = b.id
left outer join sandbox.tbl_formResponse y
on b.id = y.id
left outer join sandbox.tbl_formType x
on y.id = x.typeId
where b.status = 'Completed'
)
select
a.id,
q.response as user,
r.response as system,
s.response as agent,
t.response as owner
from sandbox.tbl_form a
left outer join (
select * from temp x
where x.type= 'User'
) q
on a.id = q.id
left outer join (
select * from temp x
where x.type= 'System'
) r
on a.id = r.id
left outer join (
select * from temp x
where x.type= 'Agent'
) s
on a.id = s.id
left outer join (
select * from temp x
where x.type= 'Owner'
) t
on a.id = t.id;
There are issues in your query.
1) In the CTE you have three left joins without ON clause. This may cause serious performance problems because joins without ON clause are CROSS JOINS.
2) BTW where b.status = 'Completed' clause converts LEFT join with table b to the inner join though still without ON clause it multiplicates all records from a by all records from b with a where.
3) Most probably you do not need CTE at all. Just join correctly with ON clause and use case when type='User' then response end + aggregate using min() or max() by id:
select a.id
max(case when x.type='User' then y.response end) as user,
max(case when x.type='System' then y.response end) as system,
...
from sandbox.tbl_form a
left outer join sandbox.tbl_formStatus b
on a.id = b.id
left outer join sandbox.tbl_formResponse y
on b.id = y.id
left outer join sandbox.tbl_formType x
on y.id = x.typeId
where b.status = 'Completed' --if you want LEFT JOIN add --or b.status is null
group by a.id

Using LAG to Find Previous Value in Oracle

I'm trying to use the LAG function in Oracle to find the previous registration value for donors.
To see my data, I started with this query to find all registrations for the particular donor:
select registration_id, registration_date from registration r where r.person_id=52503290 order by r.registration_date desc;
Then I used the LAG function to return the previous value along with the most recent value:
select registration_id as reg_id, registration_date as reg_date,
lag(registration_date,1) over (order by registration_date) as prev_reg_date
from registration
where person_id=52503290
order by registration_date desc;
And the results are as expected:
So I thought I should be good to place the LAG function within the main query to get the previous value but for some reason, the previous value returns NULL or no value at all.
SELECT
P.Person_Id AS Person_ID,
R.Registration_Date AS Drive_Date,
LAG(R.Registration_Date,1) OVER (ORDER BY R.REGISTRATION_DATE) AS Previous_Drive_Date,
P.Abo AS Blood_Type,
DT.Description AS Donation_Type
FROM
Person P
JOIN Registration R ON P.Person_Id = R.Person_Id AND P.First_Name <> 'Pooled' AND P.First_Name <> 'IMPORT'
LEFT OUTER JOIN Drives DR ON R.Drive_Id = DR.Drive_Id AND DR.Group_Id <> 24999
LEFT OUTER JOIN Branches B ON R.Branch_Id = B.Branch_Id
LEFT OUTER JOIN Donor_Group DG on DR.Group_Id = DG.Group_Id
LEFT OUTER JOIN Donation_Type DT ON R.Donation_Type_Id = DT.DONATION_TYPE_ID
WHERE
TRUNC(R.Registration_Date) = TRUNC(SYSDATE)-1
AND R.Person_Id=52503290
ORDER BY
R.Registration_Date DESC;
Here is the result set:
Any suggestions on what I am missing here? Or why this query isn't returning the values expected?
Based on #Alex Poole's suggestions, I changed the query to look like:
SELECT * FROM (
SELECT
P.Person_Id AS Person_ID,
R.Registration_Date AS Drive_Date,
LAG(R.Registration_Date,1) OVER (partition by p.person_id ORDER BY r.registration_date) AS Previous_Drive_Date,
P.Abo AS Blood_Type,
DT.Description AS Donation_Type
FROM
Person P
JOIN Registration R ON P.Person_Id = R.Person_Id AND P.First_Name <> 'Pooled' AND P.First_Name <> 'IMPORT'
LEFT OUTER JOIN Drives DR ON R.Drive_Id = DR.Drive_Id AND DR.Group_Id <> 24999
LEFT OUTER JOIN Branches B ON R.Branch_Id = B.Branch_Id
LEFT OUTER JOIN Donor_Group DG on DR.Group_Id = DG.Group_Id
LEFT OUTER JOIN Donation_Type DT ON R.Donation_Type_Id = DT.DONATION_TYPE_ID
--WHERE R.Person_Id=52503290
)
WHERE TRUNC(Drive_Date) = TRUNC(SYSDATE)-1
ORDER BY Drive_Date DESC;
It takes about 85 seconds to pull back the first 30 rows:
My original query (before the LAG function was added) took about 2 seconds to pull back approximately 2100 records. But it was nothing but a SELECT with a couple of JOINS and one item in the WHERE clause.
Looking at the record counts, Person has almost 5.5 million records and Registration has 9.1 million records.
The lag is only applied within the rows that match the where clause filter, so you would only see the previous value if that was also yesterday.
You can apply the lag in a subquery, and then filter in an outer query:
SELECT * FROM (
SELECT
P.Person_Id AS Person_ID,
R.Registration_Date AS Drive_Date,
LAG(R.Registration_Date,1) OVER (ORDER BY R.REGISTRATION_DATE) AS Previous_Drive_Date,
P.Abo AS Blood_Type,
DT.Description AS Donation_Type
FROM
Person P
JOIN Registration R ON P.Person_Id = R.Person_Id AND P.First_Name <> 'Pooled' AND P.First_Name <> 'IMPORT'
LEFT OUTER JOIN Drives DR ON R.Drive_Id = DR.Drive_Id AND DR.Group_Id <> 24999
LEFT OUTER JOIN Branches B ON R.Branch_Id = B.Branch_Id
LEFT OUTER JOIN Donor_Group DG on DR.Group_Id = DG.Group_Id
LEFT OUTER JOIN Donation_Type DT ON R.Donation_Type_Id = DT.DONATION_TYPE_ID
WHERE R.Person_Id=52503290
)
WHERE TRUNC(Drive_Date) = TRUNC(SYSDATE)-1
ORDER BY Drive_Date DESC;

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