Question about SQL select within left join - mysql

I have a question about my SQL code. My aim is to create a table2 containing all information about people who had ever had Y >=3 based on table1.
table1
ID Y date
A 1 2020-01-01
A 2 2020-01-02
A 3 2020-01-03
A 4 2020-01-04
B 1 2020-01-01
B 2 2020-01-02
C 1 2020-01-01
C 2 2020-01-02
C 3 2020-01-03
Table2
ID Y date
A 1 2020-01-01
A 2 2020-01-02
A 3 2020-01-03
A 4 2020-01-04
C 1 2020-01-01
C 2 2020-01-02
C 3 2020-01-03
My current SQL code:
select a.ID, b.* from
(select distinct ID from table1
where Y >2) as a
left join table1 as b
on a.ID = b.ID;
It returns error 1064 saying that I have some syntax error.
Would someone kindly help me with this?

I would use exists logic here:
SELECT t1.*
FROM table1 t1
WHERE EXISTS (SELECT 1 FROM table1 t2
WHERE t2.ID = t2.ID AND t2.Y >= 3);
On MySQL 8+, we can use analytic functions:
WITH cte AS (
SELECT *, COUNT(Y >= 3) OVER (PARTITION BY ID) cnt
FROM table 1
)
SELECT ID, Y, date
FROM cte
WHERE cnt > 0;

You may use the following
select * from table1 where id in
(select id from table1 where Y >=3)

A JOIN works nicely...
SELECT DISTINCT a.*
FROM my_table a
JOIN my_table b
ON b.id = a.id
WHERE b.y >2;

Related

Join table group by with sort desc

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).

SQL Keep running count, after inner Join

I am working with a local mysql server and I have two tables.
Table A:
ID | Name
1 Joe
2 Bob
3 John
Table B:
ID | DATE | Name_ID | Point
1 2010-01-01 1 1
2 2011-01-01 1 1
3 2013-01-01 1 -1
4 2010-01-01 2 -1
5 2012-01-01 2 -1
6 2013-01-01 2 -1
7 2014-01-01 2 1
For each ID in Table A, I am trying to get the
max date and
a running count of their points. (dates and points being in Table B).
Wanted Result:
Name | Latest_Date | Point_total
John NULL NULL
Joe 2013-01-01 1
Bob 2014-01-01 -2
So far I Have this Query:
SELECT DISTINCT a.Name, b.Date, SUM(b.point) OVER (PARTITION BY b.Name_ID) Point_total FROM
TableA a
LEFT OUTER JOIN TableB b
ON a.ID = b.Name_ID
INNER JOIN (
SELECT b.Name_ID, MAX(b.Date) Latest_Date
FROM TableB b GROUP BY b.Name_ID ) m
ON (m.Latest_Date = b.Date or (b.Date is NULL)) AND
(b.Name_ID = m.Name_ID OR (b.Name_ID is NULL))
ORDER BY Name_ID;
The resulting table does output the correct max date, but the running total only takes into account the point of the entry with the max date.
What am I missing here?
This just looks like aggregation:
SELECT a.Name, MAX(b.Date), SUM(b.point) as Point_total
FROM TableA a LEFT OUTER JOIN
TableB b
ON a.ID = b.Name_ID
GROUP BY a.name
That's an aggregation query:
select a.name, max(b.date) latest_date, sum(b.point) point_total
from a
left join b on b.name_id = a.id
group by a.id, a.name
SELECT A.Name, MAX(B.Date) as Max_Date, SUM(b.point) as Total_Points
FROM TableA as A
LEFT JOIN TableB as B
ON A.ID = B.Name_ID
GROUP BY A.name

sql query join with no doubles

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

Selecting Most Recent Date Relative to Another Table

Just stumped on syntax for this...
I have Two tables in mysql & I need to fetch records from Table A when following criteria are met:
1) Name in Table A matches the name in Table B
AND
2) The price for the most recent day in Table B is less than the record in Table A
So...running the query on example tables below would fetch me these two records:
03-17-2019 Bob 8
03-20-2019 John 10
Essentially, I need to evaluate each row in Table A, check the matching name in Table B that has the most recent date relative to the record under evaluation in Table A, and then determine if the price in Table A is greater than the price for the most recent matching name in Table B. After that, I need to calculate the difference between the prices. So, in the two records above, the difference would be 2 and 4
Table A
Date | Name | Price
03-08-2019 Bob 6
03-25-2019 Bob 2
03-17-2019 Bob 8
03-20-2019 John 10
Table B
Date | Name | Price
03-16-2019 Bob 4
03-28-2019 Bob 9
03-02-2019 Bob 12
03-10-2019 John 6
Thank you for the help!
Join twice the tables, once to get the min date difference and then to get the row with the min date difference:
select a.*
from tablea a
inner join tableb b on b.name = a.name
inner join (
select a.name, min(abs(datediff(b.date, a.date))) mindatediff
from tablea a inner join tableb b
on b.name = a.name
group by a.name
) g on g.name = a.name and abs(datediff(b.date, a.date)) = g.mindatediff
See the demo.
or:
select a.*
from tablea a inner join tableb b
on b.name = a.name
where abs(datediff(b.date, a.date)) = (
select min(abs(datediff(x.date, y.date)))
from tablea x inner join tableb y
where x.name = a.name and y.name = b.name
)
See the demo.
Results:
| date | name | price |
| ---------- | ---- | ----- |
| 2019-03-17 | Bob | 8 |
| 2019-03-20 | John | 10 |
In MySQL 8+, you would use window functions
select ab.*, (price - b_price)
from (select a.*, b.price as b_price,
row_number() over (partition by a.name order by datediff(b.date, a.date) as seqnum
from a join
b
on a.name = b.name and
a.date >= b.date
) ab
where seqnum = 1;

how to select one record from multi records of another table?

I have multiple tables,
Table 1:
ID Name
1 Adam
2 Bob
...
Table 2:
ID Visit
1 2019-01-01
2 2019-01-02
1 2019-01-03
1 2019-01-04
2 2019-01-04
1 2019-01-05
...
I would like to get the most recent visit from table 2 for each name in Table 1, such as
Name LastVisit
Adam 2019-01-05
Bob 2019-01-04
I tried join them together using
select tbl1.name as "Name", MAX(tbl2.visit) as LastVisit
from table1 tbl1 join table2 tbl2 on tbl1.id=tbl2.id;
but it is not working, how should I do this?
You just need a group by:
select t1.name, MAX(t2.visit) as LastVisit
from table1 t1 join
table2 t2
on t1.id = t2.id
group by t1.name;