Using Two Columns in SQL INNER JOIN - sql

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

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 logic for inner join on (A AND B) OR NOT B

I am looking to do an inner join as follows:
SELECT a.Account
FROM TableA a
JOIN TableB b
ON (a.Account = b.Account AND a.Source IN (SELECT Source FROM TableC))
OR a.Source NOT IN (SELECT Source FROM TableC)
Is there a more simple or efficient way to accomplish this?
Some examples of the data:
TableA (Account, Source):
Account | Source
------------------
111 | florida
222 | florida
333 | georgia
444 | alabama
555 | new York
TableB (Account):
Account
-------
111
222
333
TableC (Source):
Source
-------
florida
alabama
I think that the query can be rewritten by using two left joins and a WHERE clause, to avoid repeating condition a.Source [NOT] IN (SELECT Source FROM TableC):
SELECT a.Account
FROM TableA a
LEFT JOIN TableB b ON a.Account = b.Account
LEFT JOIN TableC c ON c.Source = a.Source
WHERE
(c.Source IS NOT NULL AND b.Account IS NOT NULL)
OR c.Source IS NULL
This means: a record in TableA is selected either if both joins succeed, or if the join on TableC did not succed.
My first inclination is to move all the conditions to the where clause:
SELECT a.Account
FROM TableA a
WHERE (EXISTS (SELECT 1 FROM b WHERE a.Account = b.Account) AND
EXISTS (SELECT 1 FROM TableC c WHERE c.Source = a.Source)
) OR
NOT EXISTS (SELECT 1 FROM TableC c WHERE c.Source = a.Source);
This has the advantage that you do not need to worry about duplicate results, if b has multiple rows for an account.
With appropriate indexes (on b(Account) and c(Source)), this should have very good performance.
The one exception where a JOIN would be helpful is when b is very small relative to a.

LEFT JOIN on Oracle

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

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