How to avoid OR expansion? - oracle

How to optimize the following query if the OR operator is used in a join condition to avoid the OR expansion in terms of SQL tuning?
SELECT t1.A, t2.B, t1.C, t1.D, t2.E
FROM t1 LEFT JOIN t2
ON t1.A=t2.A OR t1.B=t2.C;

Having OR condition will ignore using indexes. So once you have the following indexes -
t1 (A, B)
t2 (A, C)
You may try below query having UNION ALL clause -
SELECT t1.A, t2.B, t1.C, t1.D, t2.E
FROM t1 LEFT JOIN t2 ON t1.A=t2.A
UNION ALL
SELECT t1.A, t2.B, t1.C, t1.D, t2.E
FROM t1 LEFT JOIN t2 ON t1.B=t2.C;
This query will use index and might perform faster.

Related

USING multiple 'OR' conditions in JOIN component in Oracle data Integrator 12c

I want to use multiple 'OR' conditions in JOIN component in Oracle data Integrator 12c.
Conditions to be taken care when doing the above task is:
Say table T1 and T2, I need to take left outer join on T1(i.e. I need all the records from T1 for multiple satisfied join conditions specified in JOIN component in ODI 12c)
For example:
a. For table T1, T2: say conditions c1, c2, c3. T1 Left outer join T2.
b. I want to get the data in table say T3: Ensuring all records from T1 PLUS all records from T2 for all the conditions satisfied(namely c1,c2,c3).
Sample query:
select T1.*
from T1 LEFT OUTER JOIN T2
ON (C1 OR C2 OR C3);
Kindly help me on this at the earliest.
Thanks in advance!
You can try either query both will get you all the rows from T1 that either matched with T2 columns respectively or didn't have any match with T2.
Using UNION
SELECT DISTINCT *
FROM (
SELECT T1.*
FROM T1
LEFT OUTER JOIN T2 ON T1.day = T2.day
UNION
SELECT T1.*
FROM T1
LEFT OUTER JOIN T2 ON T1.month = T2.month
UNION
SELECT T1.*
FROM T1
LEFT OUTER JOIN T2 ON T1.yearly = T2.yearly
) as T3;
Using OR (NOTE: displaying T2 columns just to show that LEFT JOIN is working on each condition)
SELECT T1.*, T2.*
FROM T1
LEFT OUTER JOIN T2 ON
(T1.day = T2.day OR T1.month = T2.month OR T1.yearly = T2.yearly)
Sample Run
I have 4 records in T1 and 3 records in T2. Records in T1 are such that 3 rows
match with exactly 1 column in T2 and 4th row doesnt match any records in T2.
Output of both the queries gets what you need.

replace a union query involving a left join

I have a left join and a Union .
I was thinking if it is possible to replace the union with a join
select t1.col1,t1.col2,t2.col1,
from t1
left join t2 on (t1.col1 = t2.col1)
union
select t3.col1,t3.col2,t2.col1
t2.col1,
from t3
left join t2 on (t3.col1 = t2.col1)
I am on Oracle 12c can this query be re-written without the union ?
I tried this but the results don't match the union query's results
select t1.col1,t3.col2,t2.col1,
from t1
left join t2 on (t1.col1 = t2.col1)
left join t3 on (t3.col1 = t2.col1 and t1.col1 = t2.col1)
Any ideas is this even possible ?
Here are a couple of alternatives for you:
Do the union of t1 and t3 first, before joining to t2:
select t.col1, t.col2, t2.col1
from (select col1, col2
from t1
union
select col1, col2
from t3) t
left outer join t2 on (t.col1 = t2.col1);
Use a full outer join of t1 and t3 first, before joining to t2:
select t.col1, t.col2, t2.col1
from (select coalesce(t1.col1, t3.col1) col1,
coalesce(t1.col2, t3.col2) col2
from t1
full outer join t3 on t1.col1 = t3.col1
and t1.col2 = t3.col2) t
left outer join t2 on (t.col1 = t2.col1);
N.B. Both are untested, since you neglected to provide any sample data in any of the three tables. You'll have to test these approaches yourself.
ETA: Also, I believe both these approaches should work for the example as given. If your real world scenario differs from that in your question, it's possible neither approach will be of use.
ETA2: Given you're wishing to tweak your query for readability, I'd go with my first proposed solution (assuming it works for you, of course!), since the union deduplicates the rows from tables t1 and t3 before joining to t2 - that way, you're reducing the work needed to do the join. Also, the union has less to check too (2 cols instead of 3).
It may have a union in it, but it's sure as heck nicer to read than my second suggestion! At least, in my opinion *{;-)

can I use dblink in two tables in clause inner join in oracle?

For example :
select t1.*
from tabla1#dblink t1
inner join tabla2#dblink t2
on t1.campo1=t2.campo1

Return non-null value from two tables in Oracle

I have two tables, T1 and T2 with same set of columns. I need to issue a query which will return me value of columns from either table whichever is not null. If both columns are null return null as the value of that column.
The columns are c1,c2,c3,cond1.
I issued the following query. The problem is that if one subquery fails the whole query fails. Somebody please help me. Probably there is another simple way.
SELECT NVL(T1.c1, T2.c1) c1,NVL(T1.c2, T2.c2) c2,NVL(T1.c3, T2.c3) c3
FROM (SELECT c1,c2,c3
FROM T1
WHERE cond1 = 'T10') T1
,(SELECT c1,c2,c3
FROM T2
WHERE cond1 = 'T200') T2 ;
You need something like this:
SELECT NVL((SELECT T1.c1
FROM T1
WHERE T1.c2 = 'T10'),
(SELECT T2.c1
FROM T2
WHERE T2.c2 = 'T200')) AS c1
FROM dual
Or you may prefer a full outer join:
SELECT NVL(T1.c1, T2.c1) AS c1
FROM T1 FULL OUTER JOIN T2 ON 1=1
WHERE T1.c2 = 'T10'
AND T2.c2 = 'T200'
Your result is logical. If the first table is null no combination of values will exist in the natural join.
EDIT. After some new requirements we can use a hack to get the row. Lets get all three possibilities, T1, T2 or all nulls and select the first one:
SELECT *
FROM ( (SELECT T1.*
FROM T1
WHERE T1.c2 = 'T10')
UNION ALL
(SELECT T2.*
FROM T2
WHERE T2.c2 = 'T200')
UNION ALL
(SELECT T2.*
FROM dual
LEFT JOIN T1 ON 1 = 0 ) )
WHERE ROWNUM = 1

left outer join on nullable field with between in join condition (Oracle)

I have two tables as: table1 with fields c1 and dt(nullable); table2 with fields start_dt, end_dt and wk_id. Now I need to perform left outer join between the table1 and table2 to take wk_id such that dt falls between start_dt and end_dt. I applied following condition but some wk_id which shouldn't be NULL are pulled NULL and some rows get repeated.
where nvl(t1.dt,'x') between nvl(t2.start_dt(+), 'x') and nvl(t2.end_dt(+), 'x');
What is wrong with the condition?
select *
from table1 t1
left join table2 t2
on t1.dt between t2.start_dt and t2.end_dt
I recommend you try the new ANSI join syntax.
Also, are you just using 'x' as an example? Or are the dt columns really stored as strings?
It seems you are missing the part "table1 left outer join table2 on table1.some_field = table2.some_field"
Something like this:
select t1.c1, t1.dt, t2.start_dt, t2.end_dt, t2.wk_id
from table1 t1 left outer join table2 t2
on t1.some_field1 = t2.some_field1
where nvl(t1.dt,'x')
between nvl(t2.start_dt, 'x') and
nvl(t2.end_dt, 'x')

Resources