A table with 2 foreign keys referencing the same table [duplicate] - sql

This question already has an answer here:
2 Foreign Keys referencing the same Primary Key in MySQL
(1 answer)
Closed 4 years ago.
I have a table with 2 forign keys referencing the same table. How would like print out the join contents? I'm using SQLite
TableA
S.NO Col1 Col2
1 | a | b
2 | f | g
3 | d | e
TableB
S.NO Col3
a | apple
b | ball
f | frog
g | grape
d | dog
e | eat
How do i print out the following?
S.NO |Col1 | Col2 | Col3| Col4
1 | a |apple | b | ball
2 | f |frog | g | grape
3 | d |dog | e | eat
I tried
SELECT TableA.Col1, TableB.Col3, TableA.Col1
FROM TableA, TableB
WHERE TableA.Col1 = TableB.Col3
AND TableA.Col2 = TableB.Col3
Thanks for your help.

Two inner joins will do:
select
a.sno, a.col1, b1.col3, a.col2, b2.col3
from tablea a
join tableb b1 on b1.sno = a.col1
join tableb b2 on b2.sno = a.col2
Note tableb is joined twice. In this case you need to give it a different alias each time: b1 and b2. Otherwise there would be no clear way of referencing columns from each one.

Related

Oracle SQL: Exclude IDs from another table without subquery join

I would like to know if the following is possible without joining the same table twice:
Table A:
+----+------+
| ID | ColA |
+----+------+
| 1 | A1 |
| 2 | A2 |
| 3 | A3 |
| 4 | A4 |
+----+------+
Table B:
+----+------+
| ID | ColB |
+----+------+
| 1 | B1 |
| 2 | B2 |
| 3 | B3 |
| 4 | B4 |
| 5 | B5 |
| 6 | B6 |
+----+------+
Table C:
+----+
| ID |
+----+
| 1 |
| 2 |
+----+
Desired result: (A LEFT JOIN B WITHOUT C)
+----+------+------+
| ID | ColA | ColB |
+----+------+------+
| 3 | A3 | B3 |
| 4 | A4 | B4 |
+----+------+------+
So basically I need to add Column B to Table A, hence left join, and exclude all IDs which occur in Table C.
Current solution:
SELECT a.id, a.ColA, b.ColB
FROM tableA a
LEFT JOIN tableB b ON a.id = b.id
WHERE a.id NOT IN(
SELECT a2.id FROM tableA a2
LEFT JOIN tableC c on a2.id = c.id)
What's irritating me is, that the exclusion of table C requires an additional left join of table A with table C. Isn't there a more straight-forward approach, without having to join table A again as part of the subquery, if all I want to do is to exclude IDs which occur in table C from the resultset?.
Thanks
Use a not exists:
SELECT a.id, a.ColA, b.ColB
FROM tableA a
LEFT JOIN tableB b ON a.id = b.id
where not exists(select 1 from tablec c where a.id = c.id)
The issue with using a not in with a select in Oracle is that:
a) it has to return the whole subquery dataset
b) if there are nulls, it breaks
TOM link regarding these 2 issues
won't this work?
SELECT a.id, a.ColA, b.ColB
FROM tableA a
JOIN tableB b ON a.id = b.id
WHERE a.id NOT IN (SELECT c.Id FROM tableC c)
this can also be done in a join
SELECT a.id, a.ColA, b.ColB
FROM tableA a
JOIN tableB b ON a.id = b.id
LEFT JOIN tableC C ON a.id = c.id
WHERE c.Id is null

sql statement for multiple fk columns

I have two tables. The table A contains 3 columns referencing the id of table B. How do I write a select statement showing all names of table B referenced by table A.
Take a look it will explain itself pretty well:
table a is like this
----------------------
aid | bfk1 | bfk2 | bfk3
-----------------------
1 | 1 | 3 | 4
2 | 1 | 4 | 4
3 | 1 | 4 | 4
table b holds names
----------------------
bid | name
-----------------------
1 | test1
2 | test2
3 | test3
4 | test4
how to show all data from table a with names of table b. What sql statement do I need to achive this?
thanks in advance
You need to join the same table 3 times with different alias names
select a.aid,
b1.name as b_name_1,
b2.name as b_name_2,
b3.name as b_name_3
from tableA a
left join tableB b1 on a.bfk1 = b1.bid
left join tableB b2 on a.bfk2 = b2.bid
left join tableB b3 on a.bfk3 = b3.bid

Most performant of Two SQL Double joins

I have two tables that use the same compound key and I want to join them with a left join. Is my 1st query below the right way to do this, or would joining against B a second time as in my 2nd example be better/more performant? I should also mention that these tables exist on opposite sides of a database link, so I am thinking that less joins would be better...
Table A:
key1 | key2 | field
--------------------
1 | 1 | frog
1 | 2 | fish
3 | 2 | erik
Table B:
key1 | key2 | otherField
---------------------------
1 | 1 | "three times"
1 | 3 | "rarely"
2 | 2 | "once"
Desired Result:
key1 | key2 | field | otherField
---------------------------------
1 | 1 | frog | "three times"
1 | 2 | fish | null
3 | 2 | erik | null
Method 1:
select *
from A
left join B on (A.key1 = B.key1)
where (B.key2 = A.key2 or (B.key1 is null and B.key2 is null));
Method 2:
select *
from A
left join B b1 on (A.key1 = b1.key1)
left join B b2 on (A.key2 = b2.key2)
Simply left join on both keys:
SELECT A.key1, A.key2, A.field, B.otherfield
FROM A LEFT JOIN B
ON (A.key1 = B.key1)
AND (A.key2 = B.key2);

SQL query with two columns as foreign keys of the same table

I have two tables
Table A
id ! name ! fk_1_table_B_1 ! fk_2_table_B_2
-------|------|----------------|--------------
1 | John | 1 | 3
2 | Paul | 2 | 1
3 | Anna | 4 | 2
4 | Alan ! 3 | 1
Table B
id | code
-------|------
1 | EN
2 | US
3 | FR
4 | IT
The idea is to obtain the following query
id ! name ! code (fk_1_table_B_1) ! code (fk_1_table_B_2)
-------!------!-----------------------!-----------------
1 | John | EN | FR
2 | Paul | US | EN
3 | Anna | IT | US
4 | Alan ! FR | EN
If Table A had only one FK Column from Table B I would do
SELECT tableA, name, tableB.code
FROM tableA, table B
WHERE tableA.fk_1_table_B_1 = tableB.id
How can I do this with Table A having two columns as FK from B? What should I select in the SELECT?EN
Thanks
You should join to the same table twice, giving it two different aliases:
SELECT a.id, a.name, b1.code, b2.code
FROM tableA a
JOIN tableB b1 ON b1.id = a.fk_1_table_B_1
JOIN tableB b2 ON b2.id = a.fk_2_table_B_2
Note how this query uses ANSI join syntax for better clarity: rather than listing all tables in the FROM clause, it puts each of the aliased tableBs in its own JOIN clause.
Maybe this?
select a.id, a.name,
(select b.code from B b where b.id = a.fk_1_table_B_1),
(select c.code from B c where c.id = a.fk_1_table_B_2),
from A a

Join table 1 to either column 1 or 2 from table 2 without duplicates

[MS SQL 2008]
I have tables (all columns are string names):
A: two columns relating some datafield to an owning entity
B: three columns defining a hierarchy of entities
I need to create a singe table of the whole hierarchy (including all rows not existing in both tables), but the key column in table A (shown as Acol2) can be in either column 1 or 2 of table B...
A: B:
Acol1 | Acol2 Bcol1 | Bcol2 | Bcol3
-------+------ --------+-------+------
A | B B | X | Y
C | D Q | X | Y
E | F H | D | Z
G | H W | V | U
The output should be
Hierarchy:
Acol1 | Bcol1 | Bcol2 | Bcol3
-------+-------+-------+------
A | B | X | Y
Null | Q | X | Y
C | Null | D | Z
G | H | D | Z
E | Null | Null | Null
Null | W | V | U
Logic (also added to original):
If A has no record in B, show A with all Null
If A has record in Bcol1, show A with full row B
If A has record in Bcol2, show A with Null, Bcol2, Bcol3
If B has no record in A, show B with Null for Acol1
I have tried all sorts of UNIONs of two separate JOINs, but can't seem to get rid of extraneous rows...
B LEFT JOIN A ON Acol2=Bcol1 UNION B LEFT JOIN A ON Acol2=Bcol2;
gives duplicate rows, as the second part of the union has to set Bcol1 to NULL
(perhaps one solution is a way to remove this duplicate NULL row?)
B INNER JOIN A ON Acol2=Bcol1 UNION B INNER JOIN A ON Acol2=Bcol2;
Obviously removes all the rows from A and B that have no shared keys
(solution as to easy way to regain just those rows?)
Any idea appreciated!
To play:
[SQL removed - see fiddle in reply comments]
SELECT
Table1.ACol1,
CASE WHEN Table1.ACol1 = Table2.BCol1 THEN Table2.BCol1 ELSE NULL END AS BCol1
Table2.BCol2,
Table2.BCol3
FROM
Table1
FULL OUTER JOIN
Table2
ON Table1.ACol2 IN (Table2.BCol1, Table2.BCol2)
When you say no duplicates, this is only possible if ACol2 only ever appears in one field of one row in Table2. If it appears in multiple places, you'll get duplication.
- If that's possible, how would you want to chose which record from Table2?
Also, in general, however, this is a SQL-Anti-Pattern.
This is because the join would prefer an index on Table2. But, since you never know which field you're joining on, no single index will ever satsify the join condition.
EDIT:
What would make this significantly faster is to create a normalised TableB...
B_ID | B_Col | B_Val
------+-------+-------
1 | 1 | B
1 | 2 | X
1 | 3 | Y
2 | 1 | Q
2 | 2 | X
2 | 3 | Y
3 | 1 | H
3 | 2 | D
3 | 3 | Z
4 | 1 | W
4 | 2 | V
4 | 3 | U
Then index that table with (B_ID) and on (B_Val)...
Then include the B_ID field in the non_normalised table...
ID | Bcol1 | Bcol2 | Bcol3
------+-------+-------+-------
1 | B | X | Y
2 | Q | X | Y
3 | H | D | Z
4 | W | V | U
Then use the following query...
SELECT
Table1.ACol1,
CASE WHEN Table1.ACol1 = Table2.BCol1 THEN Table2.BCol1 ELSE NULL END AS BCol1
Table2.BCol2,
Table2.BCol3
FROM
(
Table1
LEFT JOIN
Table2Normalised
ON Table2Normalised.B_Val = Table1.ACol2
AND Table2Normalised.B_Col IN (1,2)
)
FULL OUTER JOIN
Table2
ON Table2Normalised.B_ID = Table2.ID
EDIT:
Without changing the schema, and instead having one index on BCol1 and a second index on Bcol2...
SELECT ACol1, BCol1, BCol2, BCol3 FROM Table1 a INNER JOIN Table2 b ON a.ACol2 = b.BCol1
UNION ALL
SELECT ACol1, NULL, BCol2, BCol3 FROM Table1 a INNER JOIN Table2 b ON a.ACol2 = b.BCol2
UNION ALL
SELECT ACol1, NULL, NULL, NULL FROM Table1 a WHERE NOT EXISTS (SELECT * FROM Table2 WHERE BCol1 = a.ACol2)
AND NOT EXISTS (SELECT * FROM Table2 WHERE BCol2 = a.ACol2)
UNION ALL
SELECT NULL, BCol1, BCol2, BCol3 FROM Table2 b WHERE NOT EXISTS (SELECT * FROM Table1 WHERE ACol2 = b.BCol1)
AND NOT EXISTS (SELECT * FROM Table1 WHERE ACol2 = b.BCol2)
But that's pretty messy...