Outer Join on multiple tables - mysql

select table3.tid, table3.name, talble4.name1, table4.name2 from table3 left join
(select table1.tid, table1.name as name1, table2.name as name2 from table1 left join table2
on table1.tid = table2.tid
union
select table2.tid, table1.name, table2.name from table1 right join table2
on table1.tid = table2.tid) as table4;
Please tell me what is wrong here.
I want a full outer join of the 3 tables : table1, table2 and table3 (which MYSQL does not support)

I would emulate a "full outer join" of three tables with three separate queries, with the rows concatenated together with UNION ALL operations.
The first query is all the tid values from table1. The second query gets all the tid values from table2 which don't exist in table1. The third query gets all the tid values in table3 which don't exist in table1 and don't exist in table2. The "trick" in the second and third queries is including appropriate
tid IS NULL
predicates in the WHERE clause, to make sure the tid values returned from the prior queries are omitted. (If we aren't guaranteed that tid is NOT NULL, we would probably want to avoid returning NULL values for tid by including an appropriate tid IS NOT NULL predicate in each of the queries for the "driving" table, which in the example below is the table following the FROM keyword.)
The last step is to include the name columns in the select list. For consistency, I'd put the name value from table1 in the same column. (In the second query, the name1 column will always be NULL, in the third query, the name1 and name2 columns will always be NULL.)
SELECT a.tid
, a.name AS name1
, b.name AS name2
, c.name AS name3
FROM table1 a
LEFT
JOIN table2 b ON b.tid = a.tid
LEFT
JOIN table3 c ON c.tid = a.tid
UNION ALL
SELECT d.tid
, f.name AS name1
, d.name AS name2
, e.name AS name3
FROM table2 d
LEFT
JOIN table3 e ON e.tid = d.tid
LEFT
JOIN table1 f ON f.tid = d.tid
WHERE f.tid IS NULL
UNION ALL
SELECT g.tid
, h.name AS name1
, i.name AS name2
, g.name AS name3
FROM table3 g
LEFT
JOIN table1 h ON h.tid = g.tid
LEFT
JOIN table2 i ON i.tid = g.tid
WHERE h.tid IS NULL
AND i.tid IS NULL

Hi you don't seem to have an "on table3.JoinColumn = table4.JoinColumn" after table4 in your SQL. I think you've been marked down as you didn't say what your error is, and I think your question is a little vague. But perhaps the SQL I've given may be all your need to complete your task...

Related

Finding Entries Without Match [duplicate]

I have these MySQL tables:
table1:
id | writer
1 | Bob
2 | Marley
3 | Michael
table2:
user_one | user_two
1 | 2
And this query:
SELECT table1.id FROM table1 LEFT JOIN table2 ON table1.id = table2.user_one
This query will return all rows of table1 which are 1,2,3
I want to select only rows which are not found in the left joint. So it should return only row with id 3
I want sort of the opposite of INNER JOIN which will select only the rows which are found in the join. How to get the opposite like if left join exists, ignore it and move to the next row. Hope i'm clear
You could use the following query:
SELECT table1.id
FROM table1
LEFT JOIN table2
ON table1.id IN (table2.user_one, table2.user_two)
WHERE table2.user_one IS NULL;
Although, depending on your indexes on table2 you may find that two joins performs better:
SELECT table1.id
FROM table1
LEFT JOIN table2 AS t1
ON table1.id = t1.user_one
LEFT JOIN table2 AS t2
ON table1.id = t2.user_two
WHERE t1.user_one IS NULL
AND t2.user_two IS NULL;
One of the best approach if you do not want to return any columns from table2 is to use the NOT EXISTS
SELECT table1.id
FROM table1 T1
WHERE
NOT EXISTS (SELECT *
FROM table2 T2
WHERE T1.id = T2.user_one
OR T1.id = T2.user_two)
Semantically this says what you want to query: Select every row where there is no matching record in the second table.
MySQL is optimized for EXISTS: It returns as soon as it finds the first matching record.
One more note to this:
When you check against a nullable column in the joined table, depending on your needs, you may have to use NOT EXISTS (or check against other columns when using LEFT JOIN), because MySQL won't be able to distinguish between a column which is NULL, but there is an existing record in the joined table and a column which is generated as NULL because the joined table have no matching record.
Here is a query that returns only the rows where no correspondance has been found in both columns user_one and user_two of table2:
SELECT T1.*
FROM table1 T1
LEFT OUTER JOIN table2 T2A ON T2A.user_one = T1.id
LEFT OUTER JOIN table2 T2B ON T2B.user_two = T1.id
WHERE T2A.user_one IS NULL
AND T2B.user_two IS NULL
There is one jointure for each column (user_one and user_two) and the query only returns rows that have no matching jointure.
Hope this will help you.
SELECT table1.id
FROM table1
LEFT JOIN table2 ON table1.id = table2.user_one
WHERE table2.user_one is NULL
Try following query:-
SELECT table1.id
FROM table1
where table1.id
NOT IN (SELECT user_one
FROM Table2
UNION
SELECT user_two
FROM Table2)
Hope this helps you.
Try:
SELECT A.id FROM
(
SELECT table1.id FROM table1
LEFT JOIN table2 ON table1.id = table2.user_one
WHERE table2.user_one IS NULL
) A
JOIN (
SELECT table1.id FROM table1
LEFT JOIN table2 ON table1.id = table2.user_two
WHERE table2.user_two IS NULL
) B
ON A.id = B.id
See Demo
Or you could use two LEFT JOINS with aliases like:
SELECT table1.id FROM table1
LEFT JOIN table2 A ON table1.id = A.user_one
LEFT JOIN table2 B ON table1.id = B.user_two
WHERE A.user_one IS NULL
AND B.user_two IS NULL
See 2nd Demo

How to find IDs that do not have data from another table

I am trying to write a query that will pull up IDs that do not have certain data in their table. Currently, we are trying to find people that belong in a certain table but are missing data.
For example, we want to make sure everyone has all three of these items
Table1
item_ID
item_1
item_2
item_3
And we have this below table
Table2
ID .. item_ID
1 .. item_1
1 .. item_2
1 .. item_3
2 .. item_1
2 .. item_2
3 .. item_1
And so on. You can see that ID 2 is missing item_3 from their table, and I want to pull a tuple that displays 2, item_3 in a query.
So far, I have something like this
SELECT DISTINCT b.ID, a.item
FROM TABLE1 a
LEFT OUTER JOIN TABLE2 b
ON a.ITEM_ID=b.ITEM_ID
WHERE b.ITEM_ID is NULL
I've been trying to write something where I can pull the item_IDs some IDs are missing, but nothing has worked so far.
SELECT a.ID, b.item_id
FROM (SELECT DISTINCT ID FROM Table2) AS a
-- or TableX AS a -- if you have a TableX with the unique ID values used for Table2
CROSS JOIN Table1 AS b
-- or (SELECT DISTINCT item_id FROM Table2) AS b -- if Table1 was not available
LEFT JOIN Table2 AS c ON a.ID = c.ID AND b.item_id = c.item_id
WHERE c.ID IS NULL
;
If you have a full list of IDs in table2 then you can use that but if you don't, you can use this:
SELECT ft.*
FROM TABLE2 t2 RIGHT OUTER JOIN
(SELECT b.ID, a.ITEM_ID
FROM TABLE1 a
CROSS JOIN (SELECT DISTINCT ID FROM TABLE2) b) ft
ON t2.id = ft.id AND t2.item_id = ft.item_id
WHERE t2.id IS NULL
http://sqlfiddle.com/#!9/2d0a19/7
I do a CROSS JOIN to get a list of what all the possible combinations should be and then choose the missing ones from the table.

MySQL: GROUP_CONCAT where rows may not exist

I have 3 tables: table1, table2 & table3
I make a select query from table1 which LEFT JOINS the other two tables. In the select I have a group_concat which takes a value from table3. Everything works well until a row with a specific row doesn't exist. The group_concat list becomes empty. Instead, I would like it to set values in the group_concat to NULL for the ones where the rows doesn't exist.
Like I said if the value in table3 exist for all the rows in table2 then it works. If not, the whole group_concat is empty.
Some "simplified" code of what I got so far:
SELECT
table1.table2Id,
table1.dateAdded,
IF(COUNT(table2.table3Id) = COUNT(*), GROUP_CONCAT(table2.table3Id), NULL) as group1,
IF(COUNT(table3.ext) = COUNT(*), GROUP_CONCAT(table3.ext), NULL) as group2
FROM table1
LEFT JOIN table2 ON
table2.id = table1.table2Id
LEFT JOIN table3 ON
table3.id = table2.table3Id
Fixed it by changing
IF(COUNT(table3.ext) = COUNT(*), GROUP_CONCAT(table3.ext), NULL) as group2
to
GROUP_CONCAT(IFNULL(table3.ext, NULL)) as group2
In your situation when you are using joins use derieved sub query in join and use IFNULL and set its default value to 0 then in the outer table this value (0) will be used if there comes null.
EDITS :
as there is no data to test you can do it like this. Use INNER JOIN instead of left join.
SELECT
table1.table2Id,
table1.dateAdded,
IF(COUNT(table2.table3Id) = COUNT(*), GROUP_CONCAT(table2.table3Id), NULL) as group1,
IF(COUNT(table3.ext) = COUNT(*), GROUP_CONCAT(table3.ext), NULL) as group2
FROM table1
INNER JOIN table2 ON
table2.id = table1.table2Id
INNER JOIN table3 ON
table3.id = table2.table3Id
Also try using derieved sub query
SELECT
table1.table2Id,
table1.dateAdded,
IF(COUNT(t2.table3Id) = COUNT(*), GROUP_CONCAT(t2.table3Id), NULL) as group1,
IF(COUNT(table3.ext) = COUNT(*), GROUP_CONCAT(table3.ext), NULL) as group2
FROM table1
LEFT JOIN (
SELECT
id,
IFNULL(table3Id,0) as table3Id,
table3Id
FROM table2
GROUP BY id table3Id
)as t2 ON t2.id = table1.table2Id
INNER JOIN table3 ON table3.id = t2.table3Id

MySQL syntax for JOIN depending on condition

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

mySQL: Multi-column join on several tables part II

I am adding a 5th table to an existing join. The original query will always return a single row because the where clause specifies a unique ID. Here are the tables we are using:
Table 1
carid, catid, makeid, modelid, caryear
Table 2
makeid, makename
Table 3
modelid, modelname
Table 4
catid, catname
Table 5
id, caryear, makename, modelname
Here is the existing query I am using:
SELECT a.*, e.citympg, e.hwympg
FROM table1 a
JOIN table2 b on a.makeid=b.makeid
JOIN table3 c on a.modelid=c.modelid
JOIN table4 d on a.catid=d.catid
JOIN table5 e on b.makename = e.make
and c.modelname = e.model
and a.caryear = e.year
WHERE a.carid = $carid;
There are 2 issues that I need to solve -
When there is no match on table 5, it does not return any results. It would seem that I need to do some sort of left join or split the query and do a union.
When there is a match on table 5, it returns multiple rows. Since the criteria that would return a single row is not being used, I would settle for an average of citympg and hwympg.
Can both objectives be achieved with a single query? How?
Assuming I understand what you want correctly... This query will constrain the results from table5 to one row per combination of the join criteria, returning average city/hwy mpg.
SELECT a.*, e.citympg, e.hwympg
FROM table1 a
JOIN table2 b on a.makeid=b.makeid
JOIN table3 c on a.modelid=c.modelid
JOIN table4 d on a.catid=d.catid
LEFT JOIN (SELECT year, make, model,
AVG(citympg) as citympg,
AVG(hwympg) as hwympg
FROM table5
GROUP BY year, make, model) e on b.makename = e.make
and c.modelname = e.model
and a.caryear = e.year
WHERE a.carid = $carid;
Note that it will return NULL mpg values when no record in table5 exists.
The usual approach is to use correlated subqueries like this:
SELECT a.*
, (SELECT avg(e.citympg)
FROM table5 e
WHERE e.make = b.makename
AND e.model = c.modelname
AND e.year = a.caryear
) as citympg
, (SELECT avg(e.hwympg)
FROM table5 e
WHERE e.make = b.makename
AND e.model = c.modelname
AND e.year = a.caryear
) as hwympg
FROM table1 a
JOIN table2 b on a.makeid=b.makeid
JOIN table3 c on a.modelid=c.modelid
JOIN table4 d on a.catid=d.catid
WHERE a.carid = $carid