Oracle try second condition only if first one gave nothing in join - sql

I'm looking in Oracle for a way to do something like if first condition has no match, do the second one. Let's get to an example.
I have this JOIN in a query :
SELECT A.COL1, A.COL2, B.COL1, B.COL2
FROM A
FULL JOIN (SELECT COL1, COL2 FROM B)
ON B.COL1 = A.COL1
OR B.COL2 = A.COL2
And you see, with this OR condition I can have two created joins for the same B object. And I don't want that. What I would like is something like :
if no result with B.COL1 = A.COL1
then let's try a join with B.COL2 = A.COL2

You need to include the negation of the condition B.COL1 = A.COL1 in the second filter. Naively, this is B.COL1 <> A.COL1 but this does not handle NULL values so you need to use B.COL1 <> A.COL1 OR B.COL1 IS NULL OR A.COL1 IS NULL like this:
SELECT A.COL1, A.COL2, B.COL1, B.COL2
FROM A
FULL JOIN (SELECT COL1, COL2 FROM B)
ON ( B.COL1 = A.COL1
OR ( ( B.COL1 <> A.COL1 OR B.COL1 IS NULL OR A.COL1 IS NULL )
AND B.COL2 = A.COL2
)
)

If B.COL1 = A.COL1 then return row. Or, if B.COL1 <> A.COL1 but B.COL2 = A.COL2 also return row.
SELECT A.COL1, A.COL2, B.COL1, B.COL2
FROM A
FULL JOIN (SELECT COL1, COL2 FROM B) B
ON (B.COL1 = A.COL1)
OR (B.COL1 <> A.COL1 and B.COL2 = A.COL2)

Related

Can you use an AND statement in a JOIN in SQL

Suppose you have two tables A and B and you are trying to write a JOIN query, is the following possible:
SELECT A.col1, B.col1
FROM A JOIN B on (A.col2 = B.col2 AND B.col3 = 'hello')
Will this return a table of col1 from table A and col2 from table B where there is a match in the second column across the tables and the third column of table B is 'hello'?
I.e. it will only return rows that are matching in col2 and this is further reduced to the cases where col3 in table B is 'hello'?
Yes. You can use:
Below will Join the Records in B table (Col3='hello') with A:
SELECT A.col1, B.col1
FROM A JOIN B on (A.col2 = B.col2 AND B.col3 = 'hello')
Below will Join all Records in B table with A, And performing where at Result of A and B:
SELECT A.col1, B.col1
FROM A JOIN B on A.col2 = B.col2
WHERE B.col3 = 'hello'
Both will give the same result when no other tables joined.
Yes you can.
You can specify any kind of boolean condition in the ON clause.
It is not mandatory that any column is involved in the condition so all of the following are valid:
SELECT A.col1, B.col1 FROM A JOIN B on 1=1
SELECT A.col1, B.col1 FROM A JOIN B on B.col3 = 'hello'
SELECT A.col1, B.col1 FROM A JOIN B on (A.col2 = B.col2 AND B.col3 = 'hello')
SELECT A.col1, B.col1 FROM A JOIN B on (A.col2 = B.col2 AND B.col3 = C.col3)
SELECT A.col1, B.col1 FROM A LEFT JOIN B on (C.col3 = 'bye')
But pay attention, if you limit the condition to only key fields the optimizer engine will improve the performances very much.
For an inner join, these two statements are equivalent:
SELECT A.col1, B.col1
FROM A JOIN
B
ON A.col2 = B.col2 AND B.col3 = 'hello';
and:
SELECT A.col1, B.col1
FROM A JOIN
B
ON A.col2 = B.col2
WHERE B.col3 = 'hello';
Both should have the same execution plans as well.
Some people prefer putting filtering conditions in the WHERE clause, so the query is more clear about "conditions between tables" versus "filters on the result set". I tend to agree with this sentiment, although I'm not dogmatic about it.
OUTER JOINs are different. For an outer join, it makes a big different where the conditions go. In that case, you generally do not have a choice, so you use ON or WHERE to get the logic that you want.

Teradata wildcard join with modifying table instead of conditions

I have tablea and tableb, that I need to join.
It can happen that in b.col2, b.col3 can be value '%', which should be something like wildcard, meaninng, that in this case we can join value of b.col2 on any value of a.col2 or value b.col3 on any value a.col3.
One solution would look like this:
select a.*, b.col4, b.col5
from tablea a
left join (select col1, col2, col3, col4, col5 tableb) b
on b.col1=a.col1 and
(b.col2 = a.col2 or b.col2 = '%') and
(b.col3 = a.col3 or b.col3 = '%')
qualify 1 = row_number() over (partition by a.id order by (case when b.col2 = '%' then 2 else 1 end), (case when b.col3 = '%' then 2, else 1 end))
My problem is that because of later use in different app, I can only use simple join conditions like:
b.col1 = a.col1 and
b.col2 = a.col2 and
b.col3 = a.col3
My question is, if there is a way, how to achieve the same result as in the first solution, but using 'simple' join conditions (a.col2=b.col2) and just making changes in selection of tableb?

Teradata wildcard in join definition

I have joined tables like bellow:
select a.*, b.col4, b.col5 from table a
inner join table b
on a.col2=b.col2
and a.col3=b.col3
It can happen that in b.col2, b.col3 can be value '*', which should be something like wildcard, meaninng, that in this case we can join value of b.col2 on any value of a.col2 or value b.col3 on any value a.col3.
Would you please help me define it?
It sounds like you have a default. One method is multiple comparison:
select a.*,
coalesce(b.col4, bdef3.col4, bdef2.col4, bdef.col4) as col4, b.col5
coalesce(b.col5, bdef3.col5, bdef2.col5, bdef.col5) as col5
from tablea a left join
tableb b
on b.col2 = a.col2 and b.col3 = a.col3 left join
tableb bdef3
on b.col2 = a.col2 and b.col3 = '*' left join
tableb bdef2
on b.col2 = '*' and b.col3 = a.col3 left join
tableb bdef
on b.col2 = '*' and b.col3 = '*';
You may want a where clause if you want to guarantee some match:
where (b.col2 is not null or bdef3.col2 is not null or bdef2.col2 is not null or bdef.col2 is not null)
I think the above is more efficient, but you can express this more succinctly as:
select a.*, b.col4, b.col5
from tablea a left join
tableb b
on (b.col2 = a.col2 or b.col2 = '*') and
(b.col3 = a.col3 or b.col3 = '*')
qualify 1 = row_number() over (partition by a.id order by (case when b.col2 = '*' then 2 else 1 end), (case when b.col3 = '*' then 2, else 1 end))

Dynamic SQL in WHERE clause

SELECT A.COL1, A.COL2, A.COL3, A.COL4
FROM TABLE A, TABLE B
WHERE B.COL1 = A.COL1
AND B.COL2 = A.COL2
AND B.COL3 - A.COL3
AND B.COL4 = A.COL4
Now I want to tune the SQL query, that whenever any of the Columns in Table B has field value 'ALL' the where clause will not come into picture.
i.e. When it has a distinct value it will match with both the tables, when the field value is 'ALL' then to exclude from the where clause.
Alternatively,
I Need B.COL1= A.COL1 (When B.COL1 <> 'ALL')
Else NO WHERE clause with B.Col1 = A.Col1 (When B.COL1 = 'ALL')
Use OR wisely:
SELECT A.COL1, A.COL2, A.COL3, A.COL4
FROM TABLE A, TABLE B
WHERE (B.COL1 = A.COL1 OR B.COL1='ALL')
AND (B.COL2 = A.COL2 OR B.COL2='ALL')
...
I would also suggest learning JOIN syntax.
Hi, You can use case statement to have a condition in where clause,
SELECT A.COL1, A.COL2, A.COL3, A.COL4
FROM TABLE A, TABLE B
WHERE B.COL1 =
CASE
WHEN B.COL1 <> 'ALL' THEN A.COL1
ELSE NULL
END
AND B.COL2 = A.COL2
AND B.COL3 - A.COL3
AND B.COL4 = A.COL4
You can achieve this with just IN:
SELECT A.COL1, A.COL2, A.COL3, A.COL4
FROM TABLE A, TABLE B
WHERE B.COL1 IN (A.COL1, 'ALL')
AND B.COL2 IN (A.COL2, 'ALL')
AND B.COL3 IN (A.COL3, 'ALL')
AND B.COL4 IN (A.COL4, 'ALL')
What is actually going on may be more clear with a more verbose version using AND/OR, but the logic is exactly the same
SELECT A.COL1, A.COL2, A.COL3, A.COL4
FROM TABLE A, TABLE B
WHERE (B.COL1 = A.COL1 OR B.COL1 = 'ALL')
AND (B.COL2 = A.COL2 OR B.COL2 = 'ALL')
AND (B.COL3 = A.COL3 OR B.COL3 = 'ALL')
AND (B.COL4 = A.COL4 OR B.COL4 = 'ALL')
Simple solution:
SELECT DISTINCT A.COL1, A.COL2, A.COL3, A.COL4
FROM TABLE A
INNER JOIN TABLE B ON
(B.COL1 = A.COL1 AND B.COL2 = A.COL2 AND B.COL3 = A.COL3 AND B.COL4 = A.COL4)
OR
('ALL' IN (B.COL1, B.COL2, B.COL3, B.COL4))
but, if you work with large tables that complex filtering could slow down very much the execution, so I suggest to use a different syntax for complex JOINs
SELECT DISTINCT *
FROM (
SELECT A.COL1, A.COL2, A.COL3, A.COL4
FROM A
INNER JOIN B
ON (B.COL1 = A.COL1 AND B.COL2 = A.COL2 AND B.COL3 = A.COL3 AND B.COL4 = A.COL4)
) J1
UNION -- ALL ?
SELECT DISTINCT *
FROM (
SELECT A.COL1, A.COL2, A.COL3, A.COL4
FROM A
INNER JOIN B ON ('ALL' IN (B.COL1, B.COL2, B.COL3, B.COL4))
) J2
This one should be much faster than previous one.
Also, I wonder about row duplicates.. with that syntax each row of table A will be added to result as many times as many rows in table B contains 'ALL'
I have added the DISTINCT clause to the SELECT to avoid duplicates (same problem affects UNION operator), so if you need duplicates, remove DISTINCT and use UNION ALL instead of UNION

Oracle DELETE sql with JOIN doesn't work

My delete statement returns a 933 error in Oracle, I'm not sure what is wrong-
DELETE b
from temp a
JOIN
fact_tab b
on a.col1 = b.col1
and a.col2 = b.col2
and a.col3 = b.col3;
Both tables dont have a primary key. select statement on the same thing works-
select *
from temp a
JOIN
fact_tab b
on a.col1 = b.col1
and a.col2 = b.col2
and a.col3 = b.col3;
try this
DELETE FROM
fact_tab
WHERE
EXISTS
(
SELECT
1
FROM
temp
WHERE
temp.col1 = fact_tab.col1 AND
temp.col2 = fact_tab.col2 AND
temp.col2 = fact_tab.col2
)
Oracle doesn't allow JOIN in a DELETE statement directly like that.
You could do the delete in the following way:
DELETE
FROM fact_tab
WHERE ROWID IN
(SELECT b.rowid
FROM temp a
JOIN fact_tab b
ON a.col1 = b.col1
AND A.col2 = b.col2
AND A.col3 = b.col3
);
You could also use WHERE EXISTS clause.