mysql left join multiple tables with one-to-many relation - mysql

I created a simple test case:
CREATE TABLE `t1` (
`id` int NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
)
CREATE TABLE `t2` (
`id2` int NOT NULL AUTO_INCREMENT,
`id1` int,
PRIMARY KEY (`id2`)
)
CREATE TABLE `t3` (
`id3` int NOT NULL AUTO_INCREMENT,
`id1` int,
PRIMARY KEY (`id3`)
)
insert into t1 (id) values (1);
insert into t2 (id1) values (1),(1);
insert into t3 (id1) values (1),(1),(1),(1);
I need to select all DISTINCT data from t1 left join t2 and DISTINCT data from t1 left join t3, returning a total of 6 rows ,1 x (2 [from t2] + 4 [from t3]) = 6, but beacause of the nature of this join I get 8 rows, 1 [from t1] x 2 [from t2] x 4 [from t3] = 8.
select * from t1 left join t2 on (t1.id = t2.id1);
2 rows in set (0.00 sec)
select * from t1 left join t3 on (t1.id = t3.id1);
4 rows in set (0.00 sec)
select * from t1 left join t2 on (t1.id = t2.id1) left join t3 on (t1.id = t3.id1);
8 rows in set (0.00 sec)
select * from t1 left join t2 on (t1.id = t2.id1) union select * from t1 left join t3 on (t1.id = t3.id1);
4 rows in set (0.00 sec)
What query should I use to get just the 6 rows I need, is it posible without subquery's or I need them (It will be more complicatet in the big query where I need this) ?
I need this for a big query where I allready get data from 8 tables, but I need to get data from 2 more to get all the data I need in just one single query, but when joining the 9th table, the returned data get's duplicated (the 9th table in this simple test case would be t3, and the 8th will be t2).
I hope someone could show me the right path to follow.
Thank you.
UPDATE SOLVED:
I realy don't know how to do this test case in one select, but in my BIG query I solved it this way: beacause I used group_concat and group by, I did it by spliting a value in multipe group_concat(DISTINCT ... ) and concat all of them like this
// instead of this
... group_concat(DISTINCT concat(val1, val2, val3)) ...
// I did this
concat(group_concat(DISTINCT val1,val2), group_concat(DISTINCT val1,val3)) ...
so the distinct on small groups of value prevent all of those duplicates.

I'm not sure if you're looking for at this solution
select * from t1 left join t2 on (t1.id = t2.id1);
union all
select * from t1 left join t3 on (t1.id = t3.id1);

I think there is a small mistake in #nick rulez's query. If it is written like this it really returns 6 rows:
(SELECT * FROM t1 LEFT JOIN t2 ON (t1.id = t2.id1))
UNION ALL
(SELECT * FROM t1 LEFT JOIN t3 ON (t1.id = t3.id1))

Related

Join two tables... without JOIN

I've got two tables T1 and T2, both with a single field (id).
T1.id has values:
1
2
4
T2.id has values:
1
3
4
I need to join these tables.
Desired result:
T1 | T2
------|------
1 | 1
2 | null
null | 3
4 | 4
With JOIN I'd do it easily:
Query 1
SELECT * FROM T1 FULL JOIN T2 ON T1.id=T2.id
But due to certain reasons I can't use JOIN here. So, with a simple query like this
Query 2
SELECT * FROM T1, T2 WHERE T1.id=T2.id
I would get only two rows of data
T1 | T2
------|------
1 | 1
4 | 4
as two other rows would be omitted due to no matches in the other table.
No matter what to fill the missing matches with. It could be NULL or any other value - really anything, but I need to get those omitted rows.
Is there a way to modify Query 2 to get the desired result without using any JOIN?
PS: Real tables are different in structure, so UNION is not allowed either.
PPS: I've just given a model to point out the problem. In reality it's a "megaquery" involving many tables each having dozens of columns.
Standard way to implement FULL OUTER JOIN when only implicit joins are supported.
select t1.id t1id, t2.id t2id
from t1, t2 where t1.id = t2.id
union all
select id, null from t1
where not exists (select 1 from t2 where t2.id = t1.id)
union all
select null, id from t2
where not exists (select 1 from t1 where t1.id = t2.id)
order by coalesce(t1id, t2id)
The first SELECT produces the INNER JOIN part of the result.
The second SELECT adds the additional LEFT OUTER JOIN rows to the result.
The third SELECT adds the additional RIGHT OUTER JOIN rows to the result.
All together, a FULL OUTER JOIN is performed!
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=ec154ad243efdff2162816205fdd42b5
SELECT t1.id t1_id, t2.id t2_id
FROM ( SELECT id FROM table1
UNION DISTINCT
SELECT id FROM table2 ) t0
NATURAL LEFT JOIN table1 t1
NATURAL LEFT JOIN table2 t2

How to remove common records found in 2 tables?

For example:
table 1
x y
2 3
1 2
8 9
table 2
x y
2 3
8 9
So here I need to remove the common field found in both the table
result should be
Result
x y
1 2
only i should get a unique row
You can use NOT EXISTS :
SELECT t1.*
FROM table1 t1
WHERE NOT EXISTS (SELECT 1 FROM table2 t2 WHERE t2.X = t1.X AND t2.Y = t1.Y);
You can also use NOT EXISTS operator which is more efficient as it also considers NULL values while comparison.
select * from table1 A
where not exists (select name from table2 B where A.name=B.name)
Try this:
Demo 1
Demo 2
select x, y
from
(
select x, y
from table1
union all
select x, y
from table2
) i
group by x, y
having count(*) = 1
Assuming the task is "delete duplicated record from BOTH TABLES accounting NULLs":
DELETE t1.*, t2.*
FROM table1 t1
JOIN table2 t2 ON t1.x <=> t2.x
AND t1.y <=> t2.y;
fiddle
Assuming the task is "delete duplicated record from both tables NOT accounting NULLs":
DELETE t1.*, t2.*
FROM table1 t1
JOIN table2 t2 USING (x, y);
fiddle
If the task is "delete duplicated record from one table and store them in another table" then edit DELETE t1.*, t2.* and store only those table which records must be deleted, for example, DELETE t1.*.
Assuming this is to remove duplicate from a table that exist in another table
if all colums have non null value then:-
DELETE t1
FROM tbl_test_1 t1
INNER JOIN tbl_test_2 t2
ON t1.col_1 = t2.col_1
AND t1.col_2 = t2.col_2
WHERE t2.id is not null
Here if a particualr coulm from both table are NULL , we miss that.
So to catch colm match with NULL as well, try this:-
#to check before deletion
SELECT * ,
concat_ws("_",COALESCE(t1.col_1, 'null'),COALESCE(t1.col_2, 'null')),
concat_ws("_",COALESCE(t2.col_1,'null'),COALESCE(t2.col_2,'null'))
FROM tbl_test_1 t1
left JOIN tbl_test_2 t2
ON concat_ws("_",COALESCE(t1.col_1, 'null'),COALESCE(t1.col_2, 'null'))= concat_ws("_",COALESCE(t2.col_1, 'null'),COALESCE(t2.col_2, 'null'))
WHERE t2.id is not null
delete query
#delete query
DELETE t1
FROM tbl_test_1 t1
left JOIN tbl_test_2 t2
ON concat_ws("_",COALESCE(t1.col_1, 'null'),COALESCE(t1.col2, 'null'))= concat_ws("_",COALESCE(t2.col_1, 'null'),COALESCE(t2.col2, 'null'))
WHERE t2.id is not null

get data from two tables fetching only one row from second table

i have two tables
t1:
sno val
12 A
13 B
14 C
t2:
refid val2
13 ba
13 bb
13 bc
using the following query:
select * from t1,t2 where t1.sno=t2.refid order by t2.refid;
and the query
select * from t1 join t2 on t2.refid = ( select refid from t2 where t1.sno = t2.refid limit 1 )
I get
B ba
B bb
B bc
and so on for all the possible combinations.
I want to get only first combination instead of all three like
B ba
C ca
and so on...
I mean, I just want only first row for every three combination that I am getting as a result of join.
As per my knowledge, I suggest below two ways to get your required output:
Way1 :
Use nested query to group by records then do join
select * from t1 a join (select * from t2 group by refid) b on a.sno= b.refid
way2:
But it's better to add auto increment key for tables like t2. Once you added auto increment key that will be useful in most use cases.
First add auto increment key to your table t2 by using below command
ALTER TABLE t2 add column id int(11) key auto_increment first;
Then execute the below join query:
select * from t1 a join (select *from t2 where t2.id in(select min(id) from t2 group by refid)) b on a.sno= b.refid
select t1.val,temp.refid from t1,(select t2.refid as refid, min(t2.val2) as val2 /*Assuming "first" row criteria*/ from t2 group by t2.refid) temp where t1.sno=temp.refid order by t2.sno;
Eliminated duplicates from t2 using sub-query and joined with t1.
If you want only one value and one column from the second table, a correlated subquery is an easy solution:
select t1.*,
(select t2.val2
from t2
where t1.sno = t2.refid
order by ? -- whatever defines "first"
limit 1
) as val2
from t1
order by t1.sno;
If you only want rows where there is a match, MySQL has a nice extension for having that fits the bill:
select t1.*,
(select t2.val2
from t2
where t1.sno = t2.refid
order by ?-- whatever defines "first"
limit 1
) as val2
from t1
having val2 is not null
order by t1.sno;
select * from (select ROW_NUMBER() OVER (PARTITION BY sno ORDER BY val2 ) AS rn, * from [dbo].[t1] a
join [dbo].[t2] b on a.sno = b.refid
) t
where rn = 1

SQL Select on multiple tables to check if value exists in one table and not used in other

I have 2 tables:
table1 (id,usedcode)
table2 (codeid,uniquecode)
I want to be able to check if a certain value exists in uniquecode of Table2, but is not already used in Table1
Try using left join as below:
SELECT t2.*
FROM table2 t2 LEFT JOIN table1 t1
ON t2.uniquecode = t1.usedcode
WHERE t1.usedcode IS null
SELECT uniquecode FROM Table2
WHERE NOT EXISTS(
SELECT * FROM Table1 WHERE usedcode = uniquecode
)
In English the query is saying, "Select all unique codes from table 2 that don't exist in table 1 as a usedcode".

Best way of deleting SQL rows how i want

How can i construct a SQL query to delete how i want.
I have two tables.
Table 1.
ID: Some Random Not Significant To This Question Columns : DateTime : UserID
Table 2.
ID: Some Random Not Significant To This Question Columns : DateTime : UserID
The two tables are related by DateTime and UserID
Is there anyway i can create a query so that it deletes from table 2 if no rows in table1 have a matching DateTime & UserID.
Thanks
You can use LEFT JOIN :
DELETE table2
FROM table2 t2 LEFT JOIN table1 t1 ON t1.`DateTime` = t2.`DateTime`
AND t1.`UserID` = t2.`UserID`
WHERE t1.`UserID` IS NULL
DELETE
FROM table2 t2
WHERE NOT EXISTS
(
SELECT NULL
FROM table1 t1
WHERE (t1.userId, t1.dateTime) = (t2.userId, t2.dateTime)
)
First of all: create a backup before you delete lots of records :)
The idea:
DELETE FROM
table1
WHERE
NOT EXISTS (SELECT 1 FROM table2 WHERE table1.referenceColumn = table2.referenceColumn)
You can check which records will be deleted by replacing the DELETE with SELECT *
And now the solution
DELETE FROM
table2
WHERE
NOT EXISTS (
SELECT 1 FROM
table1
WHERE
table2.UserID = table1.UserID
AND table2.DateTime = table1.DateTime
)