i am struggling with a query,
Table1
startdate | enddate | name | examination
2020-02-01 | 2020-02-01| JohnDoe | xyz
Table2
begindate | enddate | name | mutation
2020-02-01 | 2020-02-07 | JohnDoe | Away
2020-03-01 | 2020-03-01 | JohnDoe | Away
Query:
SELECT a.begindate
, a.enddate
, IF((a.begindate BETWEEN b.begindate AND b.enddate ), b.mutation, a.exam) value
FROM table1 a
JOIN table2 b
ON a.name = b.name
Result
a.begindate | a.enddate | value
2020-02-01 | 2020-02-01 | Away
2020-02-01 | 2020-02-01 | xyz
But i expect only to see all records from table1 and if the begindate from table 1 is between the begindate and enddate then show the value of table2.mutation without double records. tried left/right joins but without result.
How can i fix this?
Use a LEFT JOIN and set the condition a.begindate between b.begindate and b.enddate in the ON clause:
select
a.begindate, a.enddate,
case when b.name is not null then b.mutation else a.exam end as value
from table1 as a
left join table2 as b on a.name = b.name and a.begindate between b.begindate and b.enddate
Instead of the case expression you could also use:
coalesce(b.mutation, a.exam)
It looks like you want to LEFT JOIN like this:
SELECT a.begindate
,a.enddate
,b.mutation
,a.exam
FROM table1 AS a
LEFT OUTER JOIN table2 AS b
ON a.name = b.name
AND a.begindate BETWEEN b.begindate AND b.enddate
If you want to combine examination and mutation into a single field use a CASE statement:
CASE WHEN b.mutation IS NOT NULL THEN b.mutation ELSE a.exam END AS value
I think you want something like this:
select t1.startdate, t1.enddate, t1.name, t2.mutation
from table1 t1 left join
table2 t2
on t1.name = t2.name and
t1.startdate <= t2.enddate and
t1.startdate >= t2.startdate
Related
I got two tables with identical structure. From those tables I need to get rows with highest value on rate column where fix_id is the same.
Table1
fix_id | rate | proc | unique_id
2 | 72 | 50 | 23_tab1
3 | 98 | 70 | 24_tab1
4 | 78 | 80 | 25_tab1
table2
fix_id | rate | proc | unique_id
2 | 75 | 999 | 23_tab2
3 | 80 | 179 | 24_tab2
4 | 82 | 898 | 25_tab2
Expected result
fix_id | rate | proc | unique_id
2 | 75 | 999 | 23_tab2
3 | 98 | 70 | 24_tab1
4 | 82 | 898 | 25_tab2
I've tried this...
Select fix_id,proc,unique_id,MAX(rate) rate from
(Select fix_id,proc,unique_id,MAX(rate) rate from table1 group by fix_id
UNION ALL SELECT fix_id,proc,unique_id,MAX(rate) rate from table2 group by fix_id ) group by fix_id
I get the highest values from rate column but the values from other columns are incorrect.
It can be done using CASE statement.
Try this query
select
(case
when T1.rate > T2.rate then T1.fix_id else T2.fix_id
end) as fix_id,
(case
when T1.rate > T2.rate then T1.rate else T2.rate
end) as rate,
(case
when T1.rate > T2.rate then T1.proc else T2.proc
end) as proc,
(case
when T1.rate > T2.rate then T1.unique_id else T2.unique_id
end) as unique_id
from table1 as T1, table2 as T2 where T1.id = T2.id
You can use row_number():
select t.*
from (select fix_id, proc, unique_id, rate,
row_number() over (partition by fix_id order by rate desc) as seqnum
from ((select fix_id, proc, unique_id, rate from table1
) union all
(select fix_id, proc, unique_id, rate from table2
)
) t
) t
where seqnum = 1;
As fix_id is unique in both tables, the answer with CASE statements (https://stackoverflow.com/a/65609931/53341) is likely the fastest (so, I've upvoted that)...
Join once
Compare rates, on each row
Pick which table to read from, on each row
For large numbers of columns, however, it's unwieldy to type all the CASE statements. So, here is a shorter version, though it probably takes twice as long to run...
SELECT t1.*
FROM table1 AS t1 INNER JOIN table2 AS t2 ON t1.fix_id = t2.fix_id
WHERE t1.rate >= t2.rate
UNION ALL
SELECT t2.*
FROM table1 AS t1 INNER JOIN table2 AS t2 ON t1.fix_id = t2.fix_id
WHERE t1.rate < t2.rate
I have 2 table
Table 1
id | value
-----------
1 | a
2 | b
3 | c
4 | d
Table 2
id | table1_id | date
------------------------
1 | 1 | 01-01-2020 1:00:00
2 | 1 | 01-01-2020 2:00:00
3 | 1 | 05-01-2020 1:00:00 (*)
4 | 2 | 05-01-2020 1:00:00
5 | 3 | 06-01-2020 1:00:00
6 | 3 | 06-01-2020 2:00:00 (*)
7 | 2 | 07-01-2020 1:00:00 (*)
I want to join table 1 to table 2. get row of table 2 is max value date and group by table1_id
Like exxample, i want get data like this
id | value | table1_id | date
-------------------------------------------------
1 | a | 1 | 05-01-2020 1:00:00
2 | b | 2 | 07-01-2020 1:00:00
3 | c | 1 | 06-01-2020 2:00:00
4 | d | NULL | NULL
I tryed like this, but not work true
SELECT tb1.*, tb2.* FROM table1 AS tb1
LEFT JOIN
( SELECT * FROM table2 ORDER BY date DESC ) AS tb2
ON tb1.id = tb2.table1_id
GROUP BY table1_id
Can someone help me ? Thanks all <3
The old school way of doing this in MySQL might be to join to a subquery which finds the maximum date in the second table for each table1_id:
SELECT
t1.id,
t1.value,
t2.table1_id,
t2.date
FROM table1 t1
LEFT JOIN
(
SELECT t2.table1_id, t2.date
FROM table2 t2
INNER JOIN
(
SELECT table1_id, MAX(date) AS max_date
FROM table2
GROUP BY table1_id
) t
ON t.table1_id = t2.table1_id AND
t.max_date = t2.date
) t2
ON t2.table1_id = t1.id;
Demo
You can try this:
SELECT id, value, table1_id, max(date) date
FROM
(SELECT t1.id, t1.value, t2.table1_id, t2.date
FROM table1 t1 LEFT JOIN table2 t2
ON t1.id = t2.table1_id
) qry
GROUP BY id, value, table1_id
You can also use window function as below
SELECT tb1.*, tb2.table1_id, tb2.date
FROM table1 AS tb1
LEFT JOIN
( SELECT table2.*,
row_number() over(partition by table1_id ORDER BY date DESC) as seq_num
FROM table2 ) AS tb2
ON tb1.id = tb2.table1_id
Where tb2.seq_num = 1 ;
Here is a demo - https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=f52a5a930411dcc04900a1a5bacfe6e9. The demo contains both NULL and not NULL versions.
I strongly recommend that you use window functions for this -- assuming you want mulple columns. This looks like:
select t1.*, t2.*
from table1 t1 left join
(select t2.*,
row_number() over (partition by table1_id order by date DESC) as seqnum
from table2 t2
) t2
on t1.id = t2.table1_id and seq_num = 1 ;
However, if you just want one column -- and the table1_id is redundant so I see no need to include it -- then a correlated subquery is often the fastest method:
select t1.*,
(select max(t2.date) from table2 t2 where t1.id = t2.table1_id)
from table1 t1;
In particular, this can take advantage of an index on table2(table1_id, date).
I have two similar tables
Table 1
| id | name | amount|
| 2 | Mike | 1000 |
| 3 | Dave | 2500 |
Table 2
| id | name | amount|
| 2 | Mike | 1200 |
| 4 | James| 2500 |
I want to query the tables to get a result like this:
| id | name | amount_table1| amount_table2|
| 2 | Mike | 1000 | 1200 |
| 3 | Dave | 2500 | |
| 4 | james| | 2500 |
UNION ALL the tables. Do GROUP BY to get one row per id/name combo.
select id, name, sum(amount1), sum(amount2)
from
(
select id, name, amount as amount1, null as amount2 from table1
union all
select id, name, null, amount from table2
) dt
group by id, name
You need to do union with left and right join
select a.id , a.name , a.amount amount_table1,b.amount amount_table2 from table1 a left join table2 b on (a.id=b.id)
union
select b.id , b.name ,a.amount,b.amount from table1 a right join table2 b on (a.id=b.id)
MySql doesn't support FULL OUTER JOIN.
But it supports LEFT & RIGHT joins and UNION.
select
t1.id, t1.name, t1.amount as amount_table1, t2.amount as amount_table2
from Table1 t1
left join Table2 t2 on t1.id = t2.id
union all
select t2.id, t2.name, t1.amount, t2.amount
from Table2 t2
left join Table1 t1 on t2.id = t1.id
where t1.id is null
The first select will get those only in Table1 and those in both.
The second select will get those only in Table2.
And the UNION glues those resultsets together.
If this were for a database that supports FULL JOIN then it would be simplified to:
select
coalesce(t1.id, t2.id) as id,
coalesce(t1.name, t2.name) as name,
t1.amount as amount_table1,
t2.amount as amount_table2
from Table1 t1
full join Table2 t2 on t1.id = t2.id
I'm having problem with the following SQL query and MySQL
SELECT
id, cpid, label, cpdatetime
FROM
mytable AS a
WHERE
id NOT IN
(
SELECT
id
FROM
mytable AS b
WHERE
a.label = b.label
AND
a.cpdatetime > b.cpdatetime
)
AND
label LIKE 'CB%'
AND
cpid LIKE :cpid
GROUP BY label
ORDER BY cpdatetime ASC
the table looks like this
1 | 170.1 | CB55 | 2013-01-01 00:00:01
2 | 135.5 | CB55 | 2013-01-01 00:00:02
3 | 135.6 | CB59 | 2013-01-01 00:00:03
4 | 135.5 | CM43 | 2013-01-01 00:00:04
5 | 135.5 | CB46 | 2013-01-01 00:00:05
6 | 135.7 | CB46 | 2013-01-01 00:00:06
7 | 170.2 | CB46 | 2013-01-01 00:00:07
I would like my query to return
3 | 135.6 | CB59
5 | 135.5 | CB46
Edit
labels are dogs/cats and cpids are temporary family keeping the dogs/cats.
Dogs/cats move from family to family.
I need to find dogs/cats who were in :userinput family but only if they were not in another family previously
I can't alter the database and just have to work with the data as they are and I'm not the one who wrote the application/database schema.
Try to avoid correlated sub queries by using LEFT JOIN:
SELECT a.id, a.cpid, a.label, a.cpdatetime
FROM mytable AS a
LEFT JOIN mytable AS b ON a.label = b.label AND a.cpdatetime > b.cpdatetime
WHERE a.label LIKE 'CB%' AND a.cpid LIKE :cpid
AND b.label IS NULL
GROUP BY a.label
ORDER BY a.cpdatetime ASC
Fiddle
If the join condition fails, the fields of the second table alias b will be set to NULL.
Alternatively, use a non-correlated sub query:
SELECT a.id, a.cpid, a.label, a.cpdatetime
FROM mytable AS a
INNER JOIN (
SELECT label, MIN(cpdatetime) AS cpdatetime
FROM mytable
WHERE label LIKE 'CB%'
GROUP BY label
) AS b ON a.label = b.label AND a.cpdatetime = b.cpdatetime
WHERE a.cpid LIKE '135%'
ORDER BY a.cpdatetime
First, you find the minimum cpdatetime for each label and then join that with the first table where you add the additional cpid condition.
I think this is really what you want to do - select the IDs that are the earliest IDs for each label, then select out of those the records with a 135 cpid and a CB label.
SELECT
A.id, cpid, A.label, cpdatetime
FROM
mytable AS a inner join
(select id, label from mytable
group by label
having min(cpdatetime)) as b
on A.label=B.label and A.id=B.id
WHERE
A.label LIKE 'CB%'
AND
cpid LIKE '135%'
GROUP BY A.label
ORDER BY cpdatetime ASC;
http://sqlfiddle.com/#!2/ccccf/16
sorry for the poorly titled post.
Say I have the following table:
C1 | C2 | c3
1 | foo | x
2 | bar | y
2 | blaz | z
3 | something| y
3 | hello | z
3 | doctor | x
4 | name | y
5 | continue | x
5 | yesterday| z
6 | tomorrow | y
I'm trying to come up w/ a sql statement which performs the following union:
1st retrieval retrieves all records w/ c3 = 'y'
2nd retrieval retrieves the first instance of a record where c3 <> 'y' and the result is not in the previous union
So, for the result, I should see:
C1 | C2
1 | foo
2 | bar
3 | something
4 | name
5 | continue
6 | tomorrow
So two questions: 1: Am I totally smoking crack where I think I can do this, and 2: (assuming I can), how do I do this?
Try this one:
SELECT a.C1, a.C2
FROM MyTable a
WHERE a.C3 = 'y'
UNION
SELECT b.C1, b.C2
FROM MyTable b
WHERE b.C3 <> 'y' AND
b.C1 not in
(
SELECT c.C1
FROM MyTable c
WHERE c.C3 = 'y'
)
UPDATE 1
by the way, why is that there is only one record of 5 in your desired result? where, in fact, there could be two.
SEE FOR DEMO 1
OR
SELECT g.C1, MIN(g.C2) C2
FROM
(SELECT a.C1, a.C2
FROM MyTable a
WHERE a.C3 = 'y'
UNION
SELECT b.C1, b.C2
FROM MyTable b
WHERE b.C3 <> 'y' AND
b.C1 not in
(
SELECT c.C1
FROM MyTable c
WHERE c.C3 = 'y'
)
) g
GROUP BY g.C1
SEE FOR DEMO 2 (yields same result with your desired result)
DEMO # Sql Fiddle.
select *
from table1
where c3 = 'y'
union all
(select table1.*
from table1
left join table1 t1
on table1.c1 = t1.c1
and t1.c3 = 'y'
where table1.c3 <> 'y'
and t1.c1 is null
-- The meaning of first becomes clear here
order by table1.c3, table1.c2
limit 1)
Note: foo is not in a list because it is marked as x.
Try this:
SELECT C1, C2
FROM Table1
Where C3 = 'y'
UNION
(
SELECT C1, C2
FROM Table1
Where C3 <> 'y' ORDER BY C1 LIMIT 1
)
ORDER BY C1