Merge tables and keeping all distinct ID values - mysql

I have multiple tables to merge and these tables may have different ID values.
For example:
Table 1:
ID Year Month Size1
A 2015 4 10
B 2015 5 20
Table 2:
ID Year Month Size2
A 2015 4 20
C 2015 5 40
Table 3:
ID Year Month Size3
D 2015 6 50
E 2015 7 50
I want the merged table to look like this:
ID Year Month Size1 Size2 Size3
A 2015 4 10 20 NULL
B 2015 5 20 NULL NULL
C 2015 5 NULL 40 NULL
D 2015 6 NULL NULL 50
E 2015 7 NULL NULL 50
I want the output ID column to include all distinct IDs from all the tables.
My guess is that this can be somehow achieved by using Full Outer Join On ID, but I wasn't quite able to produce the desired output format.

Here's another possible query that would give the result you show:
SELECT t.id, t.year, t.month,
SUM(size1) AS size1, SUM(size2) AS size2, SUM(size3) AS size3
FROM (
SELECT id, year, month, size1, NULL AS size2, NULL AS size3 FROM t1
UNION ALL
SELECT id, year, month, NULL, size2, NULL FROM t2
UNION ALL
SELECT id, year, month, NULL, NULL, size3 FROM t3
) AS t
GROUP BY t.id, t.year, t.month;

select t1.id, t1.year, t1.month, t1.size1, t2.size2, t3.size3
from table1 as t1
left outer join table2 as t2 on t1.id = t2.id and t1.year = t2.year and t1.month = t2.month
left outer join table3 as t3 on t1.id = t3.id and t1.year = t3.year and t1.month = t3.month
union
select t3.id, t3.year, t3.month, t1.size1, t2.size2, t3.size3
from table3 as t3
left outer join table1 as t1 on t3.id = t1.id and t3.year = t1.year and t3.month = t1.month
left outer join table2 as t2 on t3.id = t2.id and t3.year = t2.year and t3.month = t2.month

Related

outer join in mysql not providing the results

Hypothetical data -
tbl1 -
id
date
value1
101
2021-01-01
200
101
2021-01-03
400
tbl2 -
id
date
value2
101
2021-01-01
600
101
2021-01-02
900
My expected result is -
id
date
value1
value2
101
2021-01-01
200
600
101
2021-01-02
NaN
900
101
2021-01-03
400
NaN
select * from (select * from tbl1 where id in
(another query)) t1
left join tbl2 as t2 on t1.id = t2.id and t1.date = t2.date
union all
select * from (select * from tbl1 where id in
(another query)) t1
right join tbl2 as t2 on t1.id = t2.id and t1.date = t2.date
where t1.id is null and t1.date is null
I am unable to figure out where am I going wrong.
I think you might be overcomplicating your union query:
SELECT t1.id, t1.date, t1.value1, t2.value2
FROM tbl1 t1
LEFT JOIN tbl2 t2 ON t1.id = t2.id AND t1.date = t2.date
UNION ALL
SELECT t2.id, t2.date, t1.value1, t2.value2
FROM tbl1 t1
RIGHT JOIN tbl2 t2 ON t1.id = t2.id AND t1.date = t2.date
WHERE t1.id IS NULL
ORDER BY id, date;
Demo
Collect all present (id, `date`) pairs then join source data to it:
SELECT id, `date`, tbl1.value1, tbl2.value2
FROM ( SELECT id, `date` FROM tbl1
UNION
SELECT id, `date` FROM tbl2 ) combined
LEFT JOIN tbl1 USING (id, `date`)
LEFT JOIN tbl2 USING (id, `date`);
fiddle
The solution assumes that (id, `date`) is unique over each separate source table. If not then some aggregation must be used (SUM or MAX, depends on the logic).

SQL set LIMIT in WHERE IN

I Have table like this:
id user_id date
1 10 2018-12-13
3 11 2018-11-29
4 12 2018-12-05
My Query looks like this:
SELECT * FROM table WHERE IN(10,11,12) AND date > 2018-11-15
Now I get all records fromt table > 2018-11-15 but I need to get first user records.
I get now:
id user_id date
1 10 2018-12-13
2 10 2018-12-01
3 11 2018-11-29
4 12 2018-12-05
5 12 2018-12-06
I need like this:
id user_id date
1 10 2018-12-13
3 11 2018-11-29
4 12 2018-12-05
One approach is to join your table to a subquery which finds the latest records for each user_id:
SELECT
t1.id,
t1.user_id,
t1.date
FROM yourTable t1
INNER JOIN
(
SELECT user_id, MAX(date) AS max_date
FROM yourTable
WHERE user_id IN (10, 11, 12)
GROUP BY user_id
) t2
ON t1.user_id = t2.user_id AND t1.date = t2.max_date
WHERE
t1.user_id IN (10, 11, 12);
To get the results in your question, you need to use grouping and find the minimum id:
SELECT t1.id, t1.user_id, t1.date
FROM table t1
INNER JOIN (SELECT MIN(id) AS min_id
FROM table
WHERE user_id IN(10, 11, 12)
AND date > 2018-11-15
GROUP BY user_id
) t2 ON t1.id = t2.id
WHERE t1.user_id IN (10, 11, 12)
AND date > 2018-11-15;
Maybe a NOT EXISTS could be used for this.
SELECT *
FROM yourtable t1
WHERE user_id IN (10, 11, 12)
AND `date` > '2018-11-15'
AND NOT EXISTS
(
SELECT 1
FROM yourtable t2
WHERE t2.user_id = t1.user_id
AND t2.id < t1.id
AND t2.`date` > '2018-11-15'
);
Test on SQL Fiddle here

SQL: Selecting a minimum value per unique group and changing values

I have a table that looks like this:
ID Date Category
x 1995 A
x 1996 B
z 1995 B
z 1996 A
y 1995 B
y 1996 B
What I want to do is to set the Category to whatever the value is for the minimum date per ID. So that the final result would look like:
ID Date Category
x 1995 A
x 1996 A
z 1995 B
z 1996 B
y 1995 B
y 1996 B
Does anyone know how to do this in SQL? Thanks!
You can use a subquery:
select
id,
date,
(
select category
from mytable x
where x.id = m.id
and not exists
(
select *
from mytable older
where older.id = x.id
and older.date < x.date
)
) as oldest_category
from mytable m;
This should do it although there might be a smarter way:
select table1.id, table1.date, t3.category
from table1
join (
select t1.id, t1.category
from table1 t1
join (
select id, min(date) as min_date
from table1
group by id
) t2 on t1.id = t2.id and t1.date = t2.min_date
) t3 on table1.id = t3.id
In the query your source table is named table1. The logic is that the inner derived table limits the outer to the min(date)
Sample SQL Fiddle
Try this
declare #t table (id char(1), date int, category char(1))
insert into #t
select 'x', 1995, 'A' union all
select 'x', 1996 , 'B' union all
select 'z', 1995 , 'B' union all
select 'z', 1996 , 'A' union all
select 'y', 1995 , 'B' union all
select 'y', 1996 , 'B'
select t1.Id,t1.Date,t2.category from #t as t1 left join
(
select t1.Id,t1.Date,t1.category from #t as t1 inner join
(
select ID, min(Date) as Date from #t group by ID
) as t2 on t1.Id=t2.Id and t1.Date=t2.Date
) as t2 on t1.Id=t2.Id

MySQL select 1 row from multiple rows

Please Suggest to get single 0 type row from multiple (0 type rows) and selected row should be just before type 1 row
Emp_tbl (id,type,company_id,created_at)
1,0,121,2015-02-19 18:05
2,0,121,2015-02-19 18:15
3,0,121,2015-02-19 18:17
4,1,121,2015-02-19 19:22
5,2,121,2015-02-19 19:25
6,0,121,2015-02-19 22:05
7,0,121,2015-02-19 22:15
8,0,121,2015-02-19 22:17
9,1,121,2015-02-19 22:22
10,2,121,2015-02-19 22:25
Expected Result
3,0,121,2015-02-19 18:17
4,1,121,2015-02-19 19:22
5,2,121,2015-02-19 19:25
8,0,121,2015-02-19 22:17
9,1,121,2015-02-19 22:22
10,2,121,2015-02-19 22:25
So what you want is the MAX(Id) of the type = 0 rows for each row that has type = 1, where the Id is less. You can join and group to get that:
select max(t0.Id) Id
from Emp_tbl t1
join Emp_tbl t0 on t0.type = 0 and t0.Id < t1.Id
where t1.type = 1
group by t1.Id
The rest is just putting it together:
select *
from Emp_tbl
where type <> 0
union all
select t.*
from Emp_tbl t
join (
select max(t0.Id) Id
from Emp_tbl t1
join Emp_tbl t0 on t0.type = 0 and t0.Id < t1.Id
where t1.type = 1
group by t1.Id
) t0 on t.Id = t0.Id
SQL Fiddle demo

using a LEFT OUTER JOIN on something COALESCED

I have this table where I only want to look at AB
ID CODE COUNT
102 AB 9
101 AB 8
100 AC 23 //not important!!!!
99 AB 7
98 AB 6
97 AB 5
96 AB 0
Conversed to this
ID NEWID CODE COUNT
102 102 AB 9
101 101 AB 8
99 100 AB 7
98 99 AB 6
97 98 AB 5
96 97 AB 0
Using this query:
SELECT t.ID, #NEWID := COALESCE(#NEWID - 1, t.ID) AS NEWID, t.CODE, t.COUNT
FROM
(SELECT ID, CODE, COUNT FROM some_table WHERE CODE = 'AB' ORDER BY ID DESC) t,
(SELECT #NEWID := NULL) _uv;
http://sqlfiddle.com/#!2/e0b8b/1/0
And now I want to count the difference of each consecutive NEWID.
So
Step 1: 9 - 8 = 1
Step 2: 8 - 7 = 1
Step 3: 7 - 6 = 1
Step 4: 6 - 5 = 1
Step 5: 5 - 0 = 5
I'm used to doing this with
LEFT OUTER JOIN some_table t2 ON t.ID = ( t2.ID + 1 )
And then taking the difference between t2.count and t.count,
but now when I'm using COALESCE, I cannot select this NEWID, so the code below does not work.
LEFT OUTER JOIN some_table t2 ON t.NEWID = ( t2.NEWID + 1 )
So how should I resolve this issue?
Oh, so that's why you needed sequential IDs. Well, you can use user variables for this, too, and you don't even need NEWID! Since you're doing stuff like this, you'd do well to read up on how user variables work.
SELECT
t.ID, t.CODE, t.COUNT,
#PREVCOUNT - t.COUNT DIFFERENCE,
#PREVCOUNT := t.COUNT -- Updates for the next iteration, so it
-- must come last!
FROM
(SELECT ID, CODE, COUNT FROM some_table WHERE CODE = 'AB' ORDER BY ID DESC) t,
(SELECT #PREVCOUNT := NULL) _uv;
SQL Fiddle
Try this:
SELECT
t1.ID as ID1,
t2.ID as ID2,
t1.CODE as CODE,
t1.COUNT as C1,
t2.COUNT as C2,
t2.COUNT - t1.COUNT as DIFF
FROM
some_table t1
INNER JOIN some_table t2 ON t1.ID < t2.ID AND t1.CODE = t2.CODE
WHERE
t1.CODE='AB'
AND NOT EXISTS
(SELECT * FROM some_table t3
INNER JOIN some_table t4 ON t3.ID < t4.ID and t3.CODE = t4.CODE
WHERE
t3.CODE='AB'
AND t1.ID = t3.ID
AND t4.ID < t2.ID
)
ORDER BY t1.ID
Another way will be to use LIMIT:
SELECT
t.ID1 AS ID,
t.CODE as CODE,
t.C2-t.C1 AS DIFF
FROM
(
SELECT
t1.ID as ID1,
t1.CODE as CODE,
t1.COUNT as C1,
(SELECT t.COUNT
FROM some_table t
WHERE t.ID > t1.ID AND t.CODE=t1.CODE
ORDER BY t.ID
LIMIT 1) as C2
FROM
some_table t1
WHERE
t1.CODE='AB'
ORDER BY t1.ID) t
ORDER BY t.ID1
http://sqlfiddle.com/#!2/e0b8b/24