I learned the hard way that Access does not allow you to do full outer joins, and while reading on how to simulate one, I have begun to understand how to do so, but am having issues trying to apply this to more than just two tables.
Is it as simple as doing the following?
SELECT * FROM table1
LEFT JOIN table2 ON table1.field = table2.field
LEFT JOIN table3 ON table1.field = table3.field
UNION
SELECT * FROM table1
RIGHT JOIN table2 ON table1.field = table2.field
RIGHT JOIN table3 ON table1.field = table3.field
A quick search of the web revealed that the subject of simulating a FULL OUTER JOIN ("FOJ") has been discussed many times on Stack Overflow and elsewhere, but the answers all seem to limit themselves to the case with only two tables. I suspected it was because a FOJ on three or more tables was
not a very common occurrence, and
potentially quite messy.
Still, I thought I'd give it a go, and here's what I came up with:
The technique most commonly mentioned for simulating a FOJ on two tables is to UNION ALL the three possible cases:
In_Table2 In_Table1
--------- ---------
false true
true false
true true
or, substituting true=1 and false=0
In_Table2 In_Table1
--------- ---------
0 1
1 0
1 1
That looks like a two-bit integer with three possible non-zero values, and the SQL to extract those would be of the form
Table2 RIGHT JOIN Table1 WHERE Table2.something IS NULL
UNION ALL
Table2 LEFT JOIN Table1 WHERE Table1.something IS NULL
UNION ALL
Table2 INNER JOIN Table1
The state table for the case with three tables would therefore look like a three-bit integer with seven possible non-zero values
In_Table3 In_Table2 In_Table1
--------- --------- ---------
0 0 1
0 1 0
0 1 1
1 0 0
1 0 1
1 1 0
1 1 1
I wanted to do the join on an integer value, so I just assigned them sequentially for each of the seven rows
Table3 Table2 Table1
------ ------ ------
1
2
3 3
4
5 5
6 6
7 7 7
so my test tables were
[Table1]
n txt
- ----
1 t1_1
3 t1_3
5 t1_5
7 t1_7
[Table2]
n txt
- ----
2 t2_2
3 t2_3
6 t2_6
7 t2_7
[Table3]
n txt
- ----
4 t3_4
5 t3_5
6 t3_6
7 t3_7
I created a saved query in Access named [foj12] to perform the FOJ between [Table1] and [Table2]
SELECT t1.n AS t1_n, t1.txt AS t1_txt, t2.n AS t2_n, t2.txt AS t2_txt
FROM
Table1 t1
INNER JOIN
Table2 t2
ON t1.n = t2.n
UNION ALL
SELECT t1.n AS t1_n, t1.txt AS t1_txt, t2.n AS t2_n, t2.txt AS t2_txt
FROM
Table1 t1
LEFT JOIN
Table2 t2
ON t1.n = t2.n
WHERE t2.n IS NULL
UNION ALL
SELECT t1.n AS t1_n, t1.txt AS t1_txt, t2.n AS t2_n, t2.txt AS t2_txt
FROM
Table1 t1
RIGHT JOIN
Table2 t2
ON t1.n = t2.n
WHERE t1.n IS NULL
It returns
t1_n t1_txt t2_n t2_txt
---- ------ ---- ------
1 t1_1
2 t2_2
3 t1_3 3 t2_3
5 t1_5
6 t2_6
7 t1_7 7 t2_7
Then I started working through the cases involving [Table3]. They were as follows, and correspond to the "unioned" queries in the SQL statement below.
Table3 matches both Table1 and Table2 (n=7)
Table3 matches Table1 but not Table2 (n=5)
Table3 matches Table2 but not Table1 (n=6)
rows in Table3 that don't match anything in Table1 or Table2 (n=4)
rows in FOJ(Table1,Table2) that have nothing in common with Table3 (n=1,2,3)
SELECT f.t1_n, f.t1_txt, f.t2_n, f.t2_txt, t3.n AS t3_n, t3.txt AS t3_txt
FROM
Table3 t3
INNER JOIN
foj12 f
ON t3.n = f.t1_n AND t3.n = f.t2_n
UNION ALL
SELECT f.t1_n, f.t1_txt, f.t2_n, f.t2_txt, t3.n AS t3_n, t3.txt AS t3_txt
FROM
Table3 t3
INNER JOIN
foj12 f
ON t3.n = f.t1_n
WHERE f.t2_n IS NULL
UNION ALL
SELECT f.t1_n, f.t1_txt, f.t2_n, f.t2_txt, t3.n AS t3_n, t3.txt AS t3_txt
FROM
Table3 t3
INNER JOIN
foj12 f
ON t3.n = f.t2_n
WHERE f.t1_n IS NULL
UNION ALL
SELECT NULL, NULL, NULL, NULL, t3.n AS t3_n, t3.txt AS t3_txt
FROM
Table3 t3
WHERE t3.n NOT IN (SELECT t1_n FROM foj12 WHERE t1_n IS NOT NULL)
AND t3.n NOT IN (SELECT t2_n FROM foj12 WHERE t2_n IS NOT NULL)
UNION ALL
SELECT f.t1_n, f.t1_txt, f.t2_n, f.t2_txt, NULL, NULL
FROM foj12 f
WHERE
(f.t1_n NOT IN (SELECT n FROM Table3) AND f.t2_n NOT IN (SELECT n FROM Table3))
OR
(f.t1_n NOT IN (SELECT n FROM Table3) AND f.t2_n IS NULL)
OR
(f.t1_n IS NULL AND f.t2_n NOT IN (SELECT n FROM Table3))
ORDER BY 5, 3, 1
That little beauty returns
t1_n t1_txt t2_n t2_txt t3_n t3_txt
---- ------ ---- ------ ---- ------
1 t1_1
2 t2_2
3 t1_3 3 t2_3
4 t3_4
5 t1_5 5 t3_5
6 t2_6 6 t3_6
7 t1_7 7 t2_7 7 t3_7
(Needless to say I'm not interested in adding a fourth table! :)
Comments welcome.
Related
I have 2 tables in MySQL database that I would like to join, where I would like to contain all results from table1.
My tables looks like this:
table1
id
name
1
name1
2
name2
3
name3
4
name4
5
name5
6
name6
7
name7
8
name8
table2
id
table1_id
myfield
1
3
test1
2
2
test2
3
1
test1
4
4
test2
5
5
null
6
2
null
What I am trying to achieve is to get a table which contains all the rows from table1 and only the data that is joined from table2.
This is my query:
select * from table1 as t1
left join table2 as t2 on t1.id = t2.table1_id
where myfield="test1"
group by t1.id
But this is not what I want to achieve.
What I want to achieve is to get all records from table1 and to have all related records from table2 where table2.myfield="test1". And for the other for table2.mytable to have null (if they do not fulfil table2.myfield="test1").
Any help is much appreciated!
Thanks!
move the where clause to the on clause:
select * from table1 as t1
left join table2 as t2 on t1.id = t2.table1_id
and myfield="test1"
group by t1.id
BTW: some DBMS does not allow select * with group by. So select the id and some aggregated values or remove group by id
I need to get all the records where table1.user is in table2.userid and table2.country = 8 and table2.status = 1
This is the sample data in my database
table1
id user
-- ----
1 12
2 23
3 34
4 32
5 85
6 38
table2
id userid country_id status
-- ---- ----- ----
1 12 5 1
2 12 8 1
3 85 8 1
4 38 8 0
5 38 7 1
6 23 8 1
7 23 4 1
in this case I should only get the id #3 in table2
Inner join will do the job taking only record with match in the two tables.
SELECT *
FROM table1 t1
INNER JOIN table2 t2 on t1.user = t2.userid
WHERE t2.country=8 and t2.status=1
You mean
"get all the records where table1.user is in table2.userid
with table2.country = 8 and table2.status = 1 ?"
If so, then:
Select * from table1 t1
Where Exists(Select * from table2
Where userId = t1.user
and country = 8
and status = 1)
Well to accomplish that is quite simple, you just need to make use of an inner join. An SQL JOIN clause is used to combine rows from two or more tables, based on a common field between them.
In your case, the INNER JOIN will return all rows from table2 where the join condition is met.
For a more detailed explanation and examples just visit this link: http://www.w3schools.com/sql/sql_join.asp
based on your response to Adam's approach "
I tried this but it didn't work, yes it gets the records but the id #2 in table2 is also included. what I need to get is the id #3 in table2. Thanks :) ". I changed my SQL query to:
SELECT
*
FROM
table1
INNER JOIN
table2
ON
table1.user = table2.userid
WHERE
table2.userid = 85
I assumed you only want the data of that particular userid in table2
I am trying to find duplicates in a table column only if the row is also a child of another table ex:
table 1 columns
id
Type
table 2 columns
id
table1Id
table3Id
Sample Data:
table 1:
id Type
1 aType
2 myType
3 myType
4 myType
5 myType
6 myType
table 2:
id table1Id table3Id
1 1 1
2 2 1
3 4 2
4 5 1
5 6 2
Results I'd like: (rows in table1 with same Type and table3Id)
table1Id table1Type table3Id
2 myType 1
5 myType 1
4 myType 2
6 myType 2
query I tried:
select t1.id as table1Id, t1.type as table1Type, t2.table3Id
from table1 t1 inner join
table2 t2
on t1.id = t2.table1Id inner join
table1 a
on t1.Type = a.Type and a.id <> t1.id
where t1.Type = 'myType' ;
The above query gives me hundreds of repeats of the same row, with around 500,000 rows returned.
You seem to want pairs of table3id and table1type that are the same. Here is a method that returns results in a slightly different format:
select t2.table3id, t1.type, group_concat(t1.id) as table1ids
from table1 t1 join
table2 t2
on t1.id = t2.table1id
group by t2.table3id, t1.type
having count(*) > 1;
This puts the ids with the same value in a list.
This works fine for me:
drop table if exists table1;
drop table if exists table2;
create table test_delete.table1 as
select 1 id , 'aType' typ union all
select 2 ,'myType' union all
select 3 ,'myType' union all
select 4 ,'myType' union all
select 5 ,'myType' union all
select 6 ,'myType';
create table test_delete.table2 as
select 1 id , 1 table1Id , 1 table3Id union all
select 2 ,2 , 1 union all
select 3 ,4 , 2 union all
select 4 ,5 , 1 union all
select 5 ,6 , 2;
select * from table1;
select
t1.id, t1.typ, t2.table3Id
from
table1 t1
inner join
table2 t2 on t1.id = t2.table1Id
where
t1.typ = 'myType'
order by t2.table3Id;
I have 3 tables as follows
Table1
Id Name
1 abcd
2 bcd
3 dabc
Table2
Id2 Name2
2 xyz
3 def
4 mno
Table3
Id Id2 Value
1 4 1
2 3 1
3 4 1
Now,
From table1 : I have to select all Id where Name is %abc%
From table2: I have to select Id2 where Name2 is "mno"
From Table3: I have to change value to 0 from 1 where Id's value are from Table1 and Id2 is from Table2.
Table 1:
select Id from Table1 where Name like '%abc%'
Table2 :
select Id2 from Table2 where Name2 = "mno"
Table 3:
update Table3 set Value = 0 where Id in() and Id2=
But, I dont know how to make it 1 single query. Can anyone please guide me up ?
Refer to: prior stack article
You've not explained how T1 relates to T2, So I have assumed a cross join.
Whenever you have a record in T1 with name like '%abc%' (1,3) in your data..
and whenever you have a record in T2 with a name equal to 'mno' 4 then you want the value in table 3 to be 0
so the select we generate should produce
1,4
3,4
and when we inner join this back to table 3 it only selects
Id Id2 Value
1 4 1
3 4 1
Now we generate an update based on this select as outlined in the link provided above...
UPDATE table3
INNER JOIN (
SSELECT t1.ID t1ID, t2.id t2ID
FROM table1 t1
CROSS JOIN table2
WHERE t1.name like '%abc%'
and t2.name like = 'mno') B
on B.t1ID = t3.Id
and B.t2ID = T3.ID2
SET value = 0
Giving us a result of
Id Id2 Value
1 4 0
2 3 1
3 4 0
if we select * from table3
update t3
set t3.Value = 0
from Table3 t3
inner join Table1 t1
on t3.Id = t1.Id
inner join Table2 t2
on t3.Id2 = t2.Id2
where t1.Name like '%abc%' and t2.Name2 = 'mno'
OR
update Table3
set value = 0
where Id in (select Id from Table1 where Name like '%abc%')
and Id2 in (select Id2 from Table2 where Name2 = 'mno')
You should think about UPDATE ... WHERE EXISTS as follows:
update Table3 set Value = 0
WHERE EXISTS (SELECT 1 FROM Table1 where Name LIKE '%abc%' AND Table1.Id=Table3.Id )
AND EXISTS (SELECT 1 FROM Table2 where Name2 = "mno" AND Table2.Id2=Table3.Id2)
I'm looking for a MySQL query to extract values like in the following example:
TABLE1:
ID name
25 ab
24 abc
23 abcd
22 abcde
21 abcdef
TABLE2:
ID ID_TABLE1 total
1 25 0
2 25 1
3 25 2
4 25 3
5 23 1
6 22 0
7 22 1
8 21 0
9 21 2
10 24 10
11 24 7
I want to return all TABLE1 rows where max value of total column (in TABLE2) is < 3.
So the results should be:
ID name
23 abcd
22 abcde
21 abcdef
I tried this:
SELECT t1.*
FROM TABLE1 t1
INNER JOIN (
SELECT MAX( total ) AS max_total, ID_TABLE1
FROM TABLE2
GROUP BY total, ID_TABLE1
) t2
ON t1.ID = t2.ID_TABLE1
WHERE t2.max_total < 3
but it's not the result I want.
Try this:
SELECT t1.ID, t1.name
FROM TABLE1 t1
INNER JOIN (SELECT ID_TABLE1, MAX(total) AS max_total
FROM TABLE2
GROUP BY ID_TABLE1
) t2 ON t1.ID = t2.ID_TABLE1
WHERE t2.max_total < 3;
Your inner query groups the results by id_table and by total. Since the maximum of total per total is the value itself, it makes the inner query somewhat meaningless. Just remove the total from the group by clause and you should be OK:
SELECT t1.*
FROM TABLE1 t1
INNER JOIN (
SELECT MAX( total ) AS max_total, ID_TABLE1
FROM TABLE2
GROUP BY ID_TABLE1
) t2
ON t1.ID = t2.ID_TABLE1
WHERE t2.max_total < 3
SELECT t1.*
FROM TABLE1 t1
INNER JOIN (
SELECT MAX( total ) AS max_total, ID_TABLE1
FROM TABLE2
GROUP BY ID_TABLE1
having t2.max_total < 3
) t2
ON t1.ID = t2.ID_TABLE1
Here is a way to do using left join without using any subquery and group by clauses.
select t1.* from table1 t1
join table2 t2
on t1.id = t2.id_table1
left join table2 t3 on
t2.id_table1 = t3.id_table1
and t2.total < t3.total
where t3.id is null
and t2.total < 3
Another way is
select t1.* from table1 t1
join table2 t2 on t1.id = t2.id_table1
where not exists(
select 1 from table2 t3
where t2.id_table1 = t3.id_table1
and t2.total < t3.total
)
and t2.total < 3;
SELECT t1.*
FROM TABLE1 t1
INNER JOIN (
SELECT MAX( total ) AS max_total, ID_TABLE1
FROM TABLE2
where total > 3 GROUP BY total, ID_TABLE1
) t2
ON t1.ID != t2.ID_TABLE1
There's a simpler way without using GROUP or MAX:
SELECT * FROM table1
WHERE id NOT IN (
SELECT id_table1 FROM table2 WHERE total >= 3
);
The subquery selects all rows in table2 that have a total >= 3. Then we select those rows from table1 that are not in the subquery result.