get values from multiple tables based on one foreign key - mysql

I have 4 tables, a,b,c,d where b,c, and d have a foreign key pointing to a's id. I want to create a query that grabs all data associated with this key from a,b,c and d. The catch is if b has an entry with this foreign key id, then c won't and vice versa. I have not been able to figure out a way to perform this query in one go. Is it possible in sql?

Try this:
select a.*, b.*, c.*, d.*
from a left join b on a.id = b.a_id
left join c on a.id = c.a_id
left join d on a.id = d.a_id

Related

How to join 3 tables where each has the key to the next in line

Imagine the following scenario:
There are 3 tables A, B and C.
Table A has no knowledge of either table B and table C.
Table B has a foreign key to table A.
Table C has foreign key to table B.
In table B as well as in table C there can be multiple items sharing the same foreign key value.
As you can see, the items from C are indirectly referenced to A through B.
What I want is to get all entries from A that are referenced in C but without any information from B or C in my result tables and without duplicates.
Is this even possible?
I have tried this like so but have no idea if it is correct:
select tableA.*
from tableA,
(select distinct tableB.AId as Aid
from tableB left join tableC on tableC.BId = tableB.id
group by tableB.id)
as temp
where tableA.id = temp.Aid
I am not sure if I understand it correctly, but you can try this one:
SELECT DISTINCT `A`.`id`, `A`.`value1`, `A`.`value2` FROM `A`
INNER JOIN `B` ON `B`.`id-a` = `A`.`id`
INNER JOIN `C` ON `C`.`id-b` = `B`.`id`
It returns all values from table A if there is a key on Table C which is linked to Table B with corresponding foreign key on table A
An alternative approach to Masoud's good response would be to use an exists though a correlated subquery.
The below subquery joins B to C in a correlated fashion (notice the B.IDA to A.ID and A is outside the subquery).
If we assume good database design, then A will not have duplicate records, thus we can omit a distinct here since we are not joining A to the other tables. Instead we are simply checking for the existence of an "A" record in the B table which must have a record in the C table due to the inner join. This has two advantages for performance
It doesn't have to join all the records together which would then
necessitate a distinct; thus you don't have the performance hit on
the distinct.
It can early escape. once a key value of A is found in the
subquery (B to C join) , it can stop looking and thus don't have to join all of B to all of A.
We select "1" in the subquery as we don't care what we select as the value will not be used anywhere. We're just using the coloration of A to (B JOIN C) to determine what in A to display.
SELECT A.*
FROM A
WHERE EXISTS( SELECT 1
FROM C
INNER JOIN B
on C.IDB = B.ID)
AND B.IDA = A.ID)
Taking what you tried and reviewing it:
select tableA.*
from tableA,
(select distinct tableB.AId as Aid
from tableB left join tableC on tableC.BId = tableB.id
group by tableB.id)
as temp
where tableA.id = temp.Aid
Starting with the "FROM"
You have tableA, (subquery) temp. This is a CROSS JOIN meaning all records from A will be joined to ALL records of (B JOIN C) so if you have 1000 records in A and 1000 records in the temp result then you'd be telling the database engine to generate 1000*1000 records in your result set; which then gets filtered to only include records matching in temp and A. The engine may be smart enough to avoid the cross join and optimize the query, but I find it confusing to maintain. So I would rewrite as
SELECT tableA.*
FROM tableA
INNER JOIN (SELECT distinct tableB.AId as Aid
FROM tableB left join tableC on tableC.BId = tableB.id
GROUP BY tableB.id) as temp
ON tableA.id = temp.Aid
Looking at the subquery (temp)
We don't need a group by as we are not aggregating. The distinct does bring us down to 1 record but at a cost to execution time.
So I would re-write as this:
SELECT tableA.*
FROM tableA
INNER JOIN (SELECT distinct tableB.AId as Aid
FROM tableB
LEFT JOIN tableC
on tableC.BId = tableB.id) as temp
ON tableA.id = temp.Aid
Then looking at the whole, if we change the outer query join to temp and make it an exists... using coloration we don't have the performance hit of the join, nor the distinct. and I'd switch the left join to an inner as we only want records in C and B so we'd have null in B if we left it as a "LEFT JOIN" which serve no purpose for us.
This gets me to the answer I initially provided.
SELECT tableA.*
FROM tableA
WHERE EXISTS (SELECT 1
FROM tableB
INNER JOIN tableC
on tableC.BId = tableB.id
AND tableB.AID = A.ID) as temp

LEFT JOIN with id columns for all joined tables

I have two tables that I am applying a join to. Table A has a foreign key that references rows from Table B. SQL is as follows:
SELECT *
FROM TableA AS a
LEFT JOIN TableB AS b ON a.id = b.tableAId
WHERE a.ownerId = X
I am getting the desired result except for one thing. That is when returning the rows in JSON, only one id column is shown (TableB).
Instead I want to be able to return all id columns in the JSON where duplicate columns would have a number appended to it. For example: id, id1, id2, id3 etc...
You need to specify the columns that you want, explicitly giving them aliases so the names are different. Something like this:
SELECT a.*, b.id as b_id
FROM TableA a LEFT JOIN
TableB b
ON a.id = b.tableAId
WHERE a.ownerId = X;

Is it possible to include ALL fields from joined table EXCEPT joined one?

Very often join fields have the same name in joined tables. If just join
SELECT a.*, b.* FROM a INNER JOIN b ON a.id=b.id
it will produce id field twice.
Is it possible to include ALL fields from joined table EXCEPT joined one?
UPDATE
I am using MySQL but standard way is also interesting to me!
UPDATE 2
Regarding USING syntax, how to use it with multiple joins?
SELECT * FROM
a INNER JOIN b USING (b_id)
INNER JOIN c USING (c_id)
swears table b doesn't contain c_id field, which is true, since it is inside a.
Normally I would write
SELECT * FROM
a INNER JOIN b ON a.b_id = b.b_id
INNER JOIN c ON a.c_id = c.c_id
In standard SQL this is achieved through USING
select *
from a
join b using (id);
This will return the id column only once.

delete records based on join result

I have two tables that are joined by an id.
Table A is where I define the records.
Table B is where I use the definition and add some data.
I am doing some data normalization and I have realized that on table B there are some ID that are no longer defined in table A.
If I run this query:
SELECT B.id_cred, A.id_cre from B LEFT JOIN A ON B.id_cred=A.id_cre
I see those records that are NULL on A.id_cre.
I'd like to DELETE from table B those records where the query returns null on table A?
Something like:
DELETE FROM B WHERE id IN (SELECT B.id from B LEFT JOIN A ON B.id_cred=A.id_cre WHERE a.id IS NULL)
but this query throws an error because table B is target and reference at the same time.
You can't specify target table B for UPDATE in FROM clause
Note that the join query will return 1408 rows so I need to do it in a massive way
Option 1, use NOT EXISTS:
delete from B
where not exists (select 1 from A where A.id_cre = B.id_cred)
Option 2, use a DELETE with JOIN:
delete B
from B
left join A on B.id_cred = A.id_cre
where A.id_cre is null
This should do the trick:
DELETE FROM B
WHERE NOT EXISTS (
SELECT id_cre
FROM A
WHERE B.id_cred=A.id_cre)
Just delete any row from B where the key does not exist in A.
NOTE: "A" and "B" can't be aliases, they must be the actual table names.
Hope this helps!
Why not use id_cre from A table since both have the id.
This statement:
DELETE FROM B WHERE id IN (SELECT B.id from B LEFT JOIN A ON B.id_cred=A.id_cre WHERE a.id IS NULL)
simply says that both a and b (B.id_cred=A.id_cre) are the same.

SELECT * from main table including foreign key related table

I have 3 tables.
Table A
a_id, a_name, a_description, b_id
Table B
b_id, b_name, c_id
Table C
c_id, c_name (c_name is unique hence no duplicates)
Table "A" has a foreign key 'b_id' to Table "B". Table "B" has foreign key 'c_id' to Table C
I want all Rows of table "A"(No where clause). Each row has 'b_id' so i also need row detail of that foreign key in Table "B". And row details of 'c_id' too.
How can I implement this in an efficient single query? I was using three separate queries and merging result in php. Code looked complicated. I know there is simpler and efficient way since I have just started MySQL.
I am making API that gets all these data and sends to my app.
Edit:
I am doing "SELECT *" from Table "A"
Then I am iterating the array of rows and running "Select b_name from
Table B where b_id = a.b_id"
then "Select c_name from Table B where c_id = b.c_id"
I am merging array result in the end.
What I need in result is * columns from Table A, 'b_name' from Table B and 'c_name' from Table C.
Here you need to use join
Select A.*,B.b_name as b_name,C.c_name as c_name
from A left join B on A.b_id = B.b_id left join C on B.c_id = C.c_id
Use JOIN, for example:
SELECT A.a_id, B.b_id, C.c_id FROM A JOIN B ON A.b_id = B.b_id JOIN C ON B.c_id = C.c_id
More info: http://www.w3schools.com/sql/sql_join_left.asp