LEFT JOIN on Oracle - sql

I have a problem getting to work a query with an outer join as expected.
The data is:
TABLE a:
id
1
2
3
TABLE b:
id aid
11 1
12 2
TABLE c:
id bid
21 11
22 12
The query is:
SELECT *
FROM
a
LEFT JOIN
b
ON a.id = b.aid
INNER JOIN c
ON b.id = c.bid
I need to get all a and where possible the b and c, like this:
a.id b.id b.aid c.id c.bid
1 11 1 21 11
2 12 2 22 12
3 null null null null
The INNER JOIN just being an extension of b when the prior LEFT JOIN matched.
Instead, I get:
a.id b.id b.aid c.id c.bid
1 11 1 21 11
2 12 2 22 12
Without the second INNER JOIN I get, as expected:
a.id b.id b.aid
1 11 1
2 12 2
3 null null
There seems to be a different definition of joins between Oracle and MS SQL Server. (TURNS OUT TO BE WRONG)
I can't figure out how to write the query to obtain the expected result.

This is the correct behaviour and SQL Server will return exactly the same result. See an online example here: http://rextester.com/EEGBZ41105
Because you are doing an inner join between b and c, this essentially voids the outer join between a and b.
If you look at the last row of the expected output:
a.id b.id b.aid c.id c.bid
3 null null null null
And now look at the join condition b.id = c.bid it's pretty clear that it will remove that row because the value of b.id is null due to the outer join between a and b and thus the inner join removes that row again.
You have to use an outer join for the join between b and c as well:
SELECT *
FROM a
LEFT JOIN b ON a.id = b.aid
LEFT JOIN c ON b.id = c.bid
;

Your query will be interpreted like this (note the brackets):
SELECT *
FROM
(
a
LEFT JOIN
b
ON a.id = b.aid
)
INNER JOIN c
ON b.id = c.bid
Instead, you probably meant:
SELECT *
FROM
a
LEFT JOIN
(
b
INNER JOIN c
ON b.id = c.bid
)
ON a.id = b.aid

Related

SQL Multiple left Joins: A to B then A to C

If I have 3 tables A, B and C I understand how to use left join to join them like this:
SELECT *
FROM A LEFT JOIN
B
ON A.col = B.col LEFT JOIN
C
on B.col = C.col
So A to B then B to C
How would I do this:
SELECT *
FROM A LEFT JOIN
B
ON A.col = B.col LEFT JOIN
C
on A.col = C.col
So A to B and then A to C
Would really appreciate any help
I don't see anything wrong with what you're trying to accomplish.
A simple example is at: http://sqlfiddle.com/#!9/6dbbe2/2
There are three tables (A, B, and C):
Table A
id name
1 Alice
2 Bob
3 Carol
4 Don
5 Edith
Table B
id id_A pet
1 5 Tex
2 4 Socks
3 2 Rex
4 1 Percy
5 1 Quinlan
Table C
id id_A hobby
1 1 acting
2 2 boxing
3 4 dancing
4 5 eating
Tables B and C relate to Table A through the id_A foreign key.
A query like you have:
SELECT * FROM A LEFT JOIN B
ON A.id = B.id_a LEFT JOIN C
ON A.id = C.id_a;
works just fine.

Full outer join like functionality but merging columns

TableA
id | symbolA
1 m
2 n
4 o
TableB
id | symbolB
2 p
3 q
5 r
I'd like the following result:
id | symbolA | symbolB
2 n p
1 m NULL
4 o NULL
3 NULL q
5 NULL r
This is what I've already tried:
SELECT
TableA.id,
TableB.id,
TableA.symbolA,
TableB.symbolB
FROM
TableA
FULL OUTER JOIN
TableB
ON
TableA.id = TableB.id
NewTable
TableA.id | TableA.symbolA | TableB.id | TableA.symbolB
2 n 2 p
1 m NULL NULL
4 o NULL NULL
NULL NULL 3 q
NULL NULL 5 r
I've already tried a full outer join in combination with a coalesce but it doesn't exactly give me the the above desired output. The language is BigQuery, though I'm hoping there's nothing idiosyncratic about my request that wouldn't make it SQL language agnostic. Thanks.
You are almost there. You just need to merge the two id columns.
For this, you can use coalesce():
select
coalesce(a.id, b.id) id,
a.symbola,
b.symbolb
from tablea a
full outer join tableb b on a.id = b.id
The following syntax that joins with using should also work (most databases that support full join allow this):
select
id,
a.symbola,
b.symbolb
from tablea a
full outer join tableb b using(id)

SQL Server: Subquery on a join

I have two tables with schema and data as below. Table A has an id and an associated name. Table B associates the id from Table A with a price and otherAttr. For each entry in Table A, there may be multiple entries with different prices in Table B, but otherAttr is the same for each entry.
Given an id for Table A, I would like to select the name, otherAttr, and the minimum price.
The below query returns multiple results, I need to write a query that will return a single result with the minimum price.
SELECT a.name, b.price, b.otherAttr
FROM A a
LEFT Join B b on b.idFromA = 1
WHERE a.id = 1
Table A Table B
id | name idFromA | price | otherAttr
-------- ---------------------------
1 | A 1 | 200 | abc
2 | B 1 | 300 | abc
1 | 400 | abc
2 | 20 | def
2 | 30 | def
2 | 40 | ef
I massively oversimplified my example. In addition to selecting the min price and otherAttr from Table B, I also have to select a bunch of other attributes from joins on other tables. Which is why I was thinking the Group By and Min should be a subquery of the join on Table B, as a way to avoid Grouping By all the attributes I am selecting (because the attributes being selected for vary programmatically).
The Actual query looks more like:
SELECT a.name, b.price, b.otherAttr, c.x, c.y, d.e, d.f, g.h....
FROM A a
LEFT Join B b on b.idFromA = 1
LEFT Join C c on something...
LEFT Join D d on something...
LEFT Join G g on something...
WHERE a.id = 1
To get this, you could use GROUP BY in an INNER query:
SELECT gd.name, gd.price,gd.otherAttr, c.x, c.y, d.e, d.f, g.h....
FROM
(SELECT a.id,a.name, MIN(b.price) as price, b.otherAttr
FROM A a
LEFT Join B b on b.idFromA = 1
WHERE a.id = 1
GROUP BY a.id,a.name,b.otherAttr) gd
LEFT Join B b on b.idFromA = 1
LEFT Join C c on something...
LEFT Join D d on something...
LEFT Join G g on something...
Try:-
SELECT a.name, MIN(b.price) minprice, b.otherAttr
FROM A a
LEFT Join B b ON a.Id = b.idFromA
GROUP BY a.name, b.otherAttr
HAVING a.id = 1
You could just do this instead:
SELECT a.name, MIN(b.price), MIN(b.otherAttr)
FROM TableA a
LEFT JOIN TableB b on b.idFromA = a.id
GROUP BY a.name
HAVING a.id = 1;
You need to inner join on price as well in addition to id on the subquery to intersect the right record(s) with the lowest price(s). Then TOP(1) will return only one of those records. You can avoid using TOP(1) if you can expand the conditions and group by fields in the subquery so you schema can assure only a single record is returned for that combination of attributes. Lastly, avoid left joins when intersecting sets.
SELECT TOP(1) p.id, p.price, b.OtherAttr
FROM B as b
INNER JOIN
(SELECT A.id, min(B.price) as price
FROM B
INNER JOIN A on A.id=B.idFromA and A.ID=1
GROUP BY A.id) as p on b.idFromA=p.id and b.price=p.price

Need to understand how Multiple joins - Left Outer, Right outer, Full outer joins work in a single SQL Query

I have four tables - A,B,C,D. Each table has 1 column: ID.
Data:
Table A = 1,2,3,4
Table B = 1,2,4,5
Table C = 2,3,4,5
Table D = 1,3,5,7
I need help in understanding the output of this SQL query:
select d.*, c.*, b.*,a.*
from d
left join c on d.id = c.id
right join b on b.id = c.id
full outer join a on a.id = b.id;
I am very clear till the left join, but after that when the subsequent joins are applied, I do not understand how the result changes.
As per #Pieter's answer, we can work systematically through this:
Taking just the first LEFT JOIN:
SELECT D.ID AS D, c.ID AS C
from d
left join c
on d.id = c.id
All of Ds rows are returned. NULLS are present for failed joins on C:
D C
1 NULL
3 3
5 5
7 NULL
Then, adding the right join to B:
SELECT D.ID AS D, c.ID AS C, b.ID AS B
from d
left join c
on d.id = c.id
right join b
on b.id = c.id
All of Bs rows are returned, with both C and D being NULL where the join fails.
Only 5 is common to D, C and B.
D C B
NULL NULL 1
NULL NULL 2
NULL NULL 4
5 5 5
Finally, the FULL OUTER JOIN back to A will add missing rows from either side of the JOIN.
This means the '3' from A not present in B is added back, with NULLs for the other columns
D C B A
NULL NULL 1 1
NULL NULL 2 2
NULL NULL 4 4
5 5 5 NULL
NULL NULL NULL 3
Imagine it as a SQL stack machine. Push tables onto the stack as they are encountered, left-to-right, in the FROM clause and perform the join on the two top-most tables as ON clauses are encountered. The result of each join is pushed onto the stack also as it is generated.

Using Two Columns in SQL INNER JOIN

TableA
AID AName
1 Alpha
2 Bravo
3 Charlie
TableB
BID BName
1 Delta
2 Echo
3 Foxtrot
TableC
CID AID BID
1 1 null
2 null 2
3 3 null
I am using the following SQL statement:
SELECT C.CID, A.Name AS First, B.Name AS Second FROM TableC
INNER JOIN TableA ON TableC.AID = TableA.AID
INNER JOIN TableB ON TableC.BID = TableB.BID
but displays a blank record. The expected result should be:
CID First Second
1 Alpha
2 Echo
3 Charlie
Any suggestions?
Use LEFT JOIN instead of INNER JOIN.
SELECT C.CID, A.Name AS First, B.Name AS Second
FROM TableC
LEFT JOIN TableA ON TableC.AID = TableA.AID
LEFT JOIN TableB ON TableC.BID = TableB.BID
Use LEFT JOIN for each A, B table
SELECT C.CID, A.AName, B.BName
FROM C
LEFT JOIN A ON C.AID = A.AID
LEFT JOIN B ON C.BID = B.BID