MySQL syntax for JOIN depending on condition - mysql

What would be a syntax of the following query:
Get all columns from Table1 and JOIN Table2 if matching reference (Table1ID) exists, otherwise JOIN Table3.
Simplified DB structure is more or less as below
Table1
ID Type
1 std
Table2
ID Table1ID Title Language
1 1 Test en
Table3
ID Table1ID Title Language Flag
1 1 Other en 1
Also, I now realized that Table3 will have multiple entries that refer to single Table1.id. How to limit it to return only the latest entry (with highest id) for every result?

If you don't want an entire separate set of columns for each join, this may be what you're looking for:
SELECT *
FROM (
SELECT a.ID AS Table1ID, a.Type, b.ID, b.Title, b.Language, NULL AS Flag
FROM Table1 a
JOIN Table2 b ON a.ID = b.Table1ID
UNION ALL
SELECT a.ID, a.Type, c.ID, c.Title, c.Language, c.Flag
FROM Table1 a
LEFT JOIN Table2 b ON a.ID = b.Table1ID
JOIN Table3 c ON a.ID = c.Table1ID
JOIN (
SELECT MAX(id) AS maxid
FROM Table3
GROUP BY Table1ID
) d ON c.ID = d.maxid
WHERE b.ID IS NULL
) a
ORDER BY a.Table1ID
SQLFiddle Demo

this is one way to do it.
select table1.id, table1.type, ifnull(table2.title, table3.title)
from table1
left join table2 on table1.id = table2.table1ID
left join table3 on table1.id = table3.table1ID

Related

SQL: full outer join (ambitious column name)

I have two table, t1 and t2.
-- t1
id name address
1 Tim A
2 Marta B
-- t2
id name address
1 Tim A
3 Katarina C
If I do t1 full outer join with t2
SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
UNION ALL
SELECT * FROM t1
RIGHT JOIN t2 ON t1.id = t2.id
However, the result has ambitious id, name, address.
How do I rename this so that I don't have duplicate column name?
Attempt:
SELECT name, address FROM
(SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
UNION ALL
SELECT * FROM t1
RIGHT JOIN t2 ON t1.id = t2.id) as derived_table;
return: ERROR- duplicate column name "name".
Ditch the * in the SELECT list.
Specify the list of expressions to be returned. And qualify all column references with either the table name, or preferably, a shorter table alias.
And assign an alias to the expression and that will be the name of the column in the resultset.
Also, the query shown is not equivalent to a FULL OUTER JOIN.
If the goal is return all rows from t1, and to also return rows from t2 where a matching row doesn't exist in t1, I'd do something like this...
SELECT t.id AS t_id
, t.name AS t_name
, t.addr AS t_addr
FROM t1 t
UNION ALL
SELECT s.id
, s.name
, s.addr
FROM t2 s
LEFT
JOIN t1 r
ON r.id = s.id
WHERE r.id IS NULL
Try fully qualifying it like
SELECT t1.id, t1.name, t1.address FROM t1

Returning ids of a table where all values of other table exist with this id using all() or exists()

I have three tables with following data
Table 3 :
Table1_id Table2_id
1 1
1 2
1 3
2 1
2 3
3 2
Table 2 :
Table2_id Name
1 A
2 B
3 C
Table 1 :
Table1_id Name
1 P
2 Q
3 R
I have a problem where I need to return all table1_id's which have an entry for all Table2_ids's in Table 3.
ie. I want my output to be
Table1_id
1
I found a solution using count().
But is there a way to use all() or exists() to solve the query?
Using NOT IN with excluding LEFT JOIN in a subselect with a CROSS JOIN
select *
from table1
where Table1_id not in (
select t1.Table1_id
from table1 t1
cross join table2 t2
left join table3 t3 using (Table1_id, Table2_id)
where t3.Table1_id is null
)
VS using COUNT()
select table1_id
from table3
group by table1_id
having count(1) = (select count(1) from table2)
Explanation:
The CROSS JOIN
select t1.Table1_id
from table1 t1
cross join table2 t2
represents how table3 would look like, if every item from table1 would be related to every item from table2.
A (natural) left join with table3 will show us which relations really exists. Filtering by where t3.Table1_id is null (excluding LEFT JOIN) we get the missing relations. Using that result for the NOT IN clause, we get only table1 items that have no missing relation with table2.
You can use the following query:
SELECT DISTINCT t1.*
FROM Table2 AS t2
CROSS JOIN Table1 AS t1
WHERE NOT EXISTS (SELECT 1
FROM Table3 AS t3
WHERE t1.Table1_id = t3.Table1_id AND
t2.Table2_id = t3.Table2_id)
to get Table1 records not having a complete set of entries from Table2 in Table3. Then use NOT IN to get the expected result.
Here is a solution using EXISTS and INNER JOIN.
SELECT DISTINCT t3_out.Table1_id FROM Table3 t3_out
WHERE EXISTS( SELECT 1
FROM Table2 t2 INNER JOIN Table3 t3 ON t2.Table2_id = t3.Table2_id
WHERE t3.Table1_id = t3_out.Table1_id
HAVING COUNT(DISTINCT t2.Table2_id) = 3 )

select from 2 tables with for statement in sql

i have 2 table and want to select data from them
table 1 :
id
name
table 2
id
name
table1.id
and i want a query to make this resualt:
table1.id
table1.name
count(table2.id)
this is simple and solved by this way :
SELECT
c.id as corridor_id,
c.name as corridor_name,
(SELECT COUNT( r.id ) FROM rooms AS r WHERE r.corridorid = c.id ) as room_count
FROM corridors AS c
now if i add another table like this :
table3
id
name
table2.id
and want a query like this :
table1.id
table1.name
count(table2.id)
count(table3.id)
idk how can i do such as this query, but if there is a way i'll be happy to find it, many tnx
You'll want to join them all together, and then Group them along these lines:
SELECT
t1.Id,
t1.Name,
Count(t2.Id) AS T2Count,
Count(t3.Id) AS T3Count
FROM table1 t1
JOIN table2 t2
ON t1.Id = t2.table1_id
JOIN table3 t3
ON t2.id = t3.table2_id
GROUP BY t1.Id, t1.Name
You don't need nested SELECT statement here. You can do it by grouping and to avoid double-counting you would want DISTINCT keyword:
SELECT
c.id as corridor_id,
c.name as corridor_name,
COUNT(DISTINCT r1.id),
COUNT(DISTINCT r2.id)
FROM
corridors c
JOIN rooms r ON r.corridorid = c.id
JOIN rooms2 r2 ON r2.corridorid = c.id
GROUP BY c.id
If you want to properly treat missing values (0 counts) you can also do this:
SELECT
c.id as corridor_id,
c.name as corridor_name,
IFNULL(COUNT(DISTINCT r1.id), 0),
IFNULL(COUNT(DISTINCT r2.id), 0)
FROM
corridors c
LEFT JOIN rooms r ON r.corridorid = c.id
LEFT JOIN rooms2 r2 ON r2.corridorid = c.id
GROUP BY c.id

LEFT JOIN 2 Table but will only return the 1st Record from Table2

table1
cid
itemdesc
itemprice
table2
cid
imagename
status
My 1st table is has unique cid (no duplicate) I want it to LEFT JOIN TO table2 but it has multiple rows per cid
cid imagename status
1 image1-of-cid1 test1
1 image2-of-cid1 test2
2 image1-of-cid2 test3
2 image2-of-cid2 test4
2 image3-of-cid2 test5
But I only want the Query to return the the 1st row only of the each record fom table 1
Thanks
I agree with John Woo's answer above. You need a subquery of some kind to actually retrieve the first row of table 2. Something like:
SELECT
t1.[id],
t2.*
FROM table1 AS t1
LEFT JOIN table2 AS t2
ON t2.cid = (SELECT TOP 1 cid FROM table2 WHERE cid = t1.cid)
you need to create an extra subquery that gets one imagename per cid. try this,
SELECT a.*, b.*
FROM table1 a
LEFT JOIN
(
SELECT cid, MIN(imagename) minImage
FROM table2
GROUP BY cid
) c ON a.cid = c.cid
LEFT JOIN table2 b
ON c.cid = b.cid AND
b.imageName = c.minImage
SQLFiddle Demo
Select
distinct a.cid,a.itemdesc,b.imagename,a.itemprice,b.status
from table1 a,
table2 b
where a.cid=b.cid
Try this:
SELECT a.cid, a.itemdesc, a.itemprice, b.imagename, b.status
FROM table1 a
LEFT OUTER JOIN table2 AS b ON a.cid = b.cid
GROUP BY a.cid, a.itemdesc, a.itemprice;

how to use in clause into inner join

I need a query like that ;
select a.*, b.column1 from table1 a
inner join table2 b on (b.id in (id_array))
This query returns nothing.
What I want to do: I retrieve the group of ids from table1.Then get the name column of these ids from table2. So, I use inner join for this.
Shouldn't just be the join?
select a.*, b.* from table1 a
inner join table2 b on b.id = a.id
select *
from table1, table2
where find_in_set(id, ids) > 0
select a.* , b.* from table1 a inner join table2 b on a.id = b.id
I assume that a.ids is some kind of id list, so you could use the find_in_set() function:
select *
from table1 a
join table2 b on find_in_set(b.id, a.ids) > 0