SQL full join priority - sql

Say you have 3 tables (tableA, tableB, tableC), each with an ID column and a Value column. Some of the tables' IDs match but some don't.
If you do:
SELECT tableA.ID FROM tableA
FULL JOIN tableB ON (tableA.ID = tableB.ID)
FULL JOIN tableC ON (tableA.ID = tableC.ID)
Is this different from:
SELECT tableA.ID FROM tableA
FULL JOIN tableB ON (tableA.ID = tableB.ID)
FULL JOIN tableC ON (tableB.ID = tableC.ID)
Or:
SELECT Y.ID FROM
(SELECT tableA.ID FROM tableA
FULL JOIN tableB ON (tableA.ID = tableB.ID)) X
FULL JOIN tableC ON (X.ID = tableC.ID)) Y
??? Someone please explain if there is a difference. Thanks.
[Oracle SQL Developer version 4.02.15.21]

For starters, here are all 3 statements, syntactically cleaned up:
SELECT COALESCE(a.ID,b.ID,c.ID)
FROM tableA a
FULL JOIN tableB b ON a.ID = b.ID
FULL JOIN tableC c ON a.ID = c.ID
SELECT COALESCE(a.ID,b.ID,c.ID)
FROM tableA a
FULL JOIN tableB b ON a.ID = b.ID
FULL JOIN tableC c ON b.ID = c.ID
SELECT COALESCE(X.ID,c.ID)
FROM
( SELECT COALESCE(a.ID ,b.ID) ID
FROM tableA a
FULL JOIN tableB b ON a.ID = b.ID) X
FULL JOIN tableC c ON X.ID = c.ID
Surprisingly, the syntax of the first statement produces duplicate values, but statements 2 and 3 work as advertised.
Edit: Upon further testing, statements 1 and 2 are prone to duplicates, depending on which tables overlap. Statement 3 seems to be the only solid approach.
SQLFiddle

Related

If a row does not exist, check a second column on a join

I'm trying to join a table on a column
Select * From tableA a
LEFT JOIN tableB b
ON a.key = b.foreignkey
...
If that row cannot be found I want to join it to another column in table B. Is there an efficient way to do this.
Thanks
You can use COALESCE to join on the first non-NULL column.
Select * From tableA a
LEFT JOIN tableB b
ON a.key = COALESCE(b.foreignkey, b.another_column, b.another_column2)
It will try to join with b.foreignkey first. if that is NULL then b.foreignkey and so on.
Please find the example here.
Add another LEFT JOIN:
Select *
From tableA a left join
tableB b
on a.key = b.foreignkey left join
tableC c
on a.key = c.foreignkey and b.foreignkey is null;

Conditional join in SQL Server dependent on other table values

I need make a decision which table should be use in join statement depend on values in another table
I tried using CASE and COALESCE but can't achieve any success.
TableA has A and B and C and many other columns
TableB has ID and NAME columns
TableC has ID and NAME columns
My select statement is;
Select A.D, A.E, A.F From TableA A
If A.E = 1 then the following join should be used
left outer join TableB B ON A.B = B.ID
and B.NAME should be returned in the select statement
If A.E = 2 then the following join should be used
left outer join TableC C ON A.B = C.ID
and C.NAME should be returned in the select statement
Just add your conditions to the joins, and then use a case statement to pull the correct field to your result set e.g.
select A.D, A.E, A.F
, case when B.[Name] is not null then B.[Name] else C.[Name] end [Name]
from TableA A
left outer join TableB B ON A.B = B.ID and A.E = 1
left outer join TableC C ON A.B = C.ID and A.E = 2
Join tablea with the union of tableb with an extra column with value 1 and tablec with an extra column with value 2 and apply the conditions in the ON clause:
select
a.D, a.E, a.F, u.NAME
from tablea a
left join (
select *, 1 col from tableb
union all
select *, 2 col from tablec
) u on a.B = u.id and a.E = u.col

SQL - cleaner way to select result based on max date?

I have a query that joins to several tables. Based on the column value of one table, I would like the key value of another table. But, when this key value is joined to another table (with the purpose of identifying different date values for that key), several dates appear. I would like to return the Key Value whose date is most recent when joined to another table. I have a query that works, but it is very redundant, as the sub-query is nearly identical to the main query. I didn't know if there was a technique or better way to achieve this.
Example query:
SELECT distinct TableA.key
FROM TableA a INNER JOIN TableB b
ON a.key = b.Key
INNER JOIN TableC c ON b.Key2 = c.Key2
INNER JOIN TableD d ON b.Key = d.Key
WHERE b.column1 = XYZ
AND c.column1 = 123
and d.date =
(SELECT max(d.date)
FROM TableA a INNER JOIN TableB b
ON a.Key = b.Key
INNER JOIN TableC c ON b.Key2 = c.Key2
INNER JOIN TableD d ON b.Key = d.Key
WHERE b.column1 = XYZ
AND c.column1 = 123
)
Try this. Use Top 1 with ties order by date desc
SELECT distinct TOP 1 with ties TableA.key
FROM TableA a INNER JOIN TableB b
ON a.key = b.Key
INNER JOIN TableC c ON b.Key2 = c.Key2
INNER JOIN TableD d ON b.Key = d.Key
WHERE b.column1 = XYZ
AND c.column1 = 123
order by d.date desc

oracle sql full join with table a not in

I have to tables (tbla and tblb) with each one col (id):
select * from tbla;
ID
---
1
3
5#A
select * from tblb;
ID
---
2
3
Now I need a full join:
select a.id, b.id
from tbla a
full outer join tblb b on b.id = a.id;
ID ID1
--------
1
3 3
5#A
2
... but without entries containing a #-sign in tbla.id
select a.id, b.id
from tbla a
full outer join tblb b on b.id = a.id
where a.id not like '%#%';
ID ID1
--------
1
3 3
why is the entry with id 2 from tblb missing?
Because when you do a full outer join, columns on either side can end up with a NULL value.
Explicitly check for NULL:
select a.id, b.id
from tbla a
full outer join tblb b on b.id = a.id
where a.id not like '%#%' or a.id is null;
(Originally, I had suggested moving the logic to the on clause. Alas, the full outer join keeps records in both tables, even when no records match the condition. So, moving the condition to the on clause doesn't fix anything.)
When you do outer joins, you have to do your filtering in the from clause. If you do it in the where clause your join effectively becomes an inner join.
So change this:
full outer join tblb b on b.id = a.id
where a.id not like '%#%'
to this
full outer join tblb b on b.id = a.id
and a.id not like '%#%'
You are doing a full join, but by specifying a.id in the where clause, you filter your result set afterwards.
To achieve what you want, you can either move the clause into the join condition:
select a.id, b.id
from tbla a
full outer join tblb b
on b.id = a.id
and a.id not like '%#%';
Or you can use nvl:
select a.id, b.id
from tbla a
full outer join tblb b on b.id = a.id
where nvl(a.id, 'n/a') not like '%#%';
Or explicitly allow NULL values for a.id:
select a.id, b.id
from tbla a
full outer join tblb b on b.id = a.id
where (a.id is null or a.id not like '%#%');

In SQL, What's the difference a ON condition following a Join vs at the end of multiple JOINS

I have been having a hard time googling an answer for this, but....
can someone explain to me the difference between putting the ON condition of a JOIN with the the JOIN itself vs putting the ON at the end of all the other JOINs.
here is an example http://sqlfiddle.com/#!3/e0a0f/3
CREATE TABLE TableA (Email VARCHAR(100), SomeNameA VARCHAR(100))
CREATE TABLE Tableb (Email VARCHAR(100), SomeNameB VARCHAR(100))
CREATE TABLE Tablec (Email VARCHAR(100), SomeNameC VARCHAR(100))
INSERT INTO TableA SELECT 'joe#test.com', 'JoeA'
INSERT INTO TableA SELECT 'jan#test.com', 'JaneA'
INSERT INTO TableA SELECT 'dave#test.com', 'DaveA'
INSERT INTO TableB SELECT 'joe#test.com', 'JoeB'
INSERT INTO TableB SELECT 'dave#test.com', 'DaveB'
INSERT INTO TableC SELECT 'joe#test.com', 'JoeC'
INSERT INTO TableC SELECT 'dave#test.com', 'DaveC'
SELECT TOP 2 a.*,
b.*,
c.*
FROM TableA a
LEFT OUTER JOIN TableB b
ON a.email = b.email
INNER JOIN TableC c
ON c.Email = b.email;
SELECT TOP 2 a.*,
b.*,
c.*
FROM TableA a
LEFT OUTER JOIN TableB b
INNER JOIN TableC c
ON c.Email = b.email
ON a.email = b.email;
I don't understand why these two SELECT statements produce different results.
What matters is orders of joins. Treat your expressions as if every join produced temporary "virtual" table.
So when you write
FROM TableA a
LEFT OUTER JOIN TableB b ON a.email = b.email
INNER JOIN TableC c ON c.Email = b.email ;
then order is as follows:
TableA is left joined to TableB producing temporary relation V1
V1 is inner joined to TableC.
Meanhwile when you write:
FROM TableA a
LEFT OUTER JOIN TableB b
INNER JOIN TableC c ON c.Email = b.email ON a.email = b.email;
then order is as follows:
TableB is inner joined to TableC producing temporary relation V1.
TableA is left joined to V1.
Thus results are different. It is generally recommended to use parenthesis in such situations to improve readability of the query:
FROM TableA a
LEFT OUTER JOIN
(TableB b INNER JOIN TableC c ON c.Email = b.email)
ON a.email = b.email;
In your second example, the part ON a.email = b.email belongs to the LEFT JOIN.
If written like this, it means the following:
INNER JOIN TableC with TableB and LEFT OUTER JOIN the result with TableA.
The result will be all rows from TableA joined with those rows from TableB that also have an entry in TableC.
The first example means the following:
LEFT OUTER JOIN TableB with TableA and INNER JOIN TableC with the result. This is equivalent to using an INNER JOIN for TableB.
Explanation: When you LEFT OUTER JOIN TableA with TableB you will get all rows from TableA and for matching rows in TableB you will get that data, too. In your result set you will have rows with b.email = NULL and this will now be INNER JOINed with TableC. As long as there is no entry in TableC with email = NULL you will get the results you observed.