Select only newest grouped entries - mysql

I have a table with data like this:
+-----------+-------+------+----------+
| timestamp | event | data | moreData |
+-----------+-------+------+----------+
| 100000000 | 1 | 10 | 20 |
| 100000001 | 1 | 15 | 10 |
| 100000002 | 1 | 30 | 30 |
| 100000003 | 1 | 5 | 50 |
| 100000004 | 2 | 110 | 120 |
| 100000005 | 2 | 115 | 110 |
| 100000006 | 2 | 130 | 130 |
| 100000007 | 2 | 15 | 150 |
+-----------+-------+------+----------+
Now I want to select only the newest rows for each event. So in the end I want to have this result set:
+-----------+-------+------+----------+
| timestamp | event | data | moreData |
+-----------+-------+------+----------+
| 100000003 | 1 | 5 | 50 |
| 100000007 | 2 | 15 | 150 |
+-----------+-------+------+----------+
So far I was not able to do this. In MySQL I can use "GROUP BY event" but then I get some random row from the database, not the newest. ORDER BY doesn't help because the grouping is done before ordering. Using an aggregation like MAX(timestamp) while grouping by event also doesn't help because then the timestamp is the newest but "data" and "moreData" is still from some other random row.
I guess I have to do a sub select so I have to first get the latest timestamp like this:
SELECT MAX(timestamp), event FROM mytable GROUP BY event
and then use the result set to filter a second SELECT, but how? And maybe there is a clever way to do it without a sub select?

AFAIK, sub select is your best option, as follows:
SELECT *
FROM mytable mt
JOIN ( SELECT MAX(timestamp) as max, event
FROM mytable
GROUP BY event) m_mt
ON (mt.timestamp = m_mt.max AND mt.event = m_mt.event);

You could use an inner join as a filter:
select *
from events e1
join (
select event
, max(timestamp) as maxtimestamp
from events
group by
event
) e2
on e1.event = e2.event
and e1.tiemstamp = e2.maxtimestamp

SELECT * FROM
(SELECT * FROM mytable ORDER BY timestamp DESC) AS T1
GROUP BY event;

SELECT e2.*
FROM events e
JOIN events e2 ON e2.event = e.event AND e2.timestamp = MAX(e2.timestamp)
GROUP BY e.id

Related

Mysql query returning latest result for each element

Having a hard time wrapping my mind around what seems should be a simply query.
So let's say we have a table that keeps track of amount of widgets/balloons in each store by date. How would you get a list of stores and their latest widget/balloons count?
i.e.
mysql> SELECT * FROM inventory;
+----+------------+-------+---------+---------+
| id | invDate | store | widgets | balloons|
+----+------------+-------+---------+---------+
| 1 | 2011-01-01 | 3 | 50 | 35 |
| 2 | 2011-01-04 | 2 | 50 | 35 |
| 3 | 2013-07-04 | 3 | 12 | 78 |
| 4 | 2020-07-04 | 2 | 47 | 18 |
| 5 | 2020-08-06 | 2 | 16 | NULL |
+----+------------+-------+---------+---------+
5 rows in set (0.00 sec)
Would like the result table to list all stores and their latest inventory of widgets/baloons
store, latest widgets, latest balloons
+-------+-----------+---------+
| store | widgets | baloons |
+-------+-----------+---------+
| 2 | 16 | NULL |
| 3 | 12 | 78 |
+-------+-----------+---------+
or grab latest non NULL value for balloons.
This works for all versions of MySQL
select i.*
from inventory i
join
(
select store, max(invDate) as maxDate
from inventory
group by store
) tmp on tmp.store = i.store
and tmp.maxDate = i.invDate
With MySQL 8+ you can do window functions:
with cte as
(
select store, widgets, balloons,
ROW_NUMBER() OVER(PARTITION BY store ORDER BY invDate desc) AS rn
from inventory
)
select * from cte where rn = 1
You can use a correlated sub query to get latest record for each store
select i.*
from inventory i
where i.invDate = (
select max(invDate)
from inventory
where i.store = store
)
order by i.store
DEMO

How to sum the value and retrieve the result using if condition- using MySQL?

I want to sum every time and check with if condition. If condition matches I want the get the created date of the final matched row.
+------------+----------------------------------+------------+--------+
| id | EMAIL | created | Amt |
+------------+----------------------------------+------------+--------+
| 61 | abc#gmail.com | 1514909390 | 57.00 |
| 25 | xyz#gmail.com | 1515534837 | 360.00 |
| 36 | zccc#abv.com | 1515645391 | 240.00 |
| 22 | vv#aa.com | 1516419622 | 320.40 |
| 48 | aa#xyz.com | 1516706121 | 240.00 |
+------------+----------------------------------+------------+--------+
I try this query but I'm not getting the solution...
select
sum(a.amount) as amt,
if(sum(a.amount)>8000,slp.sal_time,0) as Amt_exceed_date
from employee a
join emp_user u
on a.cmp_id=u.user_id
left join emp_sal as slp
on slp.user_id=a.cmp_id
where
order by slp.sal_time;
Somewhat like row wise sum
select e.*,(
select sum(Amt)
from employee
where created <= e.created
) row_wise_sum
from employee e
having row_wise_sum < 800
order by e.created
desc limit 1
Demo

MySQL - Return Latest Date and Total Sum from two rows in a column for multiple entries

For every ID_Number, there is a bill_date and then two types of bills that happen. I want to return the latest date (max date) for each ID number and then add together the two types of bill amounts. So, based on the table below, it should return:
| 1 | 201604 | 10.00 | |
| 2 | 201701 | 28.00 | |
tbl_charges
+-----------+-----------+-----------+--------+
| ID_Number | Bill_Date | Bill_Type | Amount |
+-----------+-----------+-----------+--------+
| 1 | 201601 | A | 5.00 |
| 1 | 201601 | B | 7.00 |
| 1 | 201604 | A | 4.00 |
| 1 | 201604 | B | 6.00 |
| 2 | 201701 | A | 15.00 |
| 2 | 201701 | B | 13.00 |
+-----------+-----------+-----------+--------+
Then, if possible, I want to be able to do this in a join in another query, using ID_Number as the column for the join. Would that change the query here?
Note: I am initially only wanting to run the query for about 200 distinct ID_Numbers out of about 10 million. I will be adding an 'IN' clause for those IDs. When I do the join for the final product, I will need to know how to get those latest dates out of all the other join possibilities. (ie, how do I get ID_Number 1 to join with 201604 and not 201601?)
I would use NOT EXISTS and GROUP BY
select, t1.id_number, max(t1.bill_date), sum(t1.amount)
from tbl_charges t1
where not exists (
select 1
from tbl_charges t2
where t1.id_number = t2.id_number and
t1.bill_date < t2.bill_date
)
group by t1.id_number
the NOT EXISTS filter out the irrelevant rows and GROUP BY do the sum.
I would be inclined to filter in the where:
select id_number, sum(c.amount)
from tbl_charges c
where c.date = (select max(c2.date)
from tbl_charges c2
where c2.id_number = c.id_number and c2.bill_type = c.bill_type
)
group by id_number;
Or, another fun way is to use in with tuples:
select id_number, sum(c.amount)
from tbl_charges c
where (c.id_number, c.bill_type, c.date) in
(select c2.id_number, c2.bill_type, max(c2.date)
from tbl_charges c2
group by c2.id_number, c2.bill_type
)
group by id_number;

MySQL SELECT * FROM ... WHERE ... ORDER BY 'which one has the latest record in other table '

I have the following tables:
chats:
+--------+-------------+--------------+--------------+---------------------+
| chatID | firstPerson | secondPerson | chatAccepted | creationDate |
+--------+-------------+--------------+--------------+---------------------+
| 1 | 59 | 52 | 1 | 31-01-2014 09:32:37 |
| 2 | 59 | 12 | 0 | 28-01-2014 11:07:25 |
| 3 | 34 | 59 | 1 | 28-01-2014 08:50:48 |
| 4 | 78 | 59 | 1 | 26-01-2014 03:58:12 |
+--------+-------------+--------------+--------------+---------------------+
messages:
+-----------+-------------+--------+----------+------------+---------------------+
| messageID | messageText | chatID | senderID | receiverID | creationDate |
+-----------+-------------+--------+----------+------------+---------------------+
| 1 | Lorum... | 1 | 59 | 52 | 31-01-2014 09:32:37 |
| 2 | Ipsum... | 1 | 52 | 59 | 28-01-2014 11:07:25 |
| 3 | Dollar... | 3 | 34 | 59 | 28-01-2014 08:50:48 |
| 4 | Sit... | 3 | 59 | 34 | 31-01-2014 11:09:48 |
+-----------+-------------+--------+----------+------------+---------------------+
What I'm trying to get as a result is the chatID where (firstPerson = 59 or secondPerson = 59) and chatAccepted = 1. Now the thing I can't get figured out: I want the result ordered by 'which one has the latest message'.
I tried a lot of different things, one was:
"SELECT chats.chatID, chats.firstPerson, chats.secondPerson, str_to_date(messages.creationDate,'%d-%m-%Y %H:%i:%s') AS cdate
FROM chats
INNER JOIN messages
ON chats.chatID=messages.chatID
WHERE chats.chatAccepted = 1 AND messages.receiverID = 59
UNION SELECT chats.chatID, chats.firstPerson, chats.secondPerson, str_to_date(messages.creationDate,'%d-%m-%Y %H:%i:%s') AS cdate
FROM chats
INNER JOIN messages
ON chats.chatID=messages.chatsID
WHERE chats.chatAccepted = 1 AND messages.senderID = 59
ORDER BY cdate desc"
This works like a charm, except for when there are no messages yet. Than it just results no records. But i need to know if that chat is accepted otherwise they can never even start the chat.
Any help would be very welcome.
If you need any more information please led me know!
UPDATE: so what I at least want as a result in this case would be:
+--------+
| chatID |
+--------+
| 3 |
| 1 |
| 4 |
+--------+
this because chatID '3' has the latest message linked to it. chatID '4' has no messages yet, but it is a accepted chat so it should be in the result.
SELECT c.chatID, c.firstPerson, c.secondPerson, str_to_date(m.creationDate,'%d-%m-%Y %H:%i:%s') AS cdate
FROM chats c left join messages m on c.chatID=m.chatID
WHERE c.chatAccepted = 1 AND
59 in (m.senderID, m.receiverID)
ORDER BY m.creationDate desc
To keep also chats whit no messages you need a LEFT JOIN then you should decide witch order have the chats without messages, suppose you want use the chat creation date, then use the COALESCE function to select chat creation date where there are no messages (message creation date is NULL).
In this way you have multiple entry for chats with more messages, than you have to use also the aggregation funcion MAX on cDate to have only last messages entries:
SELECT c.chatID,
c.firstPerson,
c.secondPerson,
str_to_date(MAX(COALESCE(m.creationDate, c.creationDate)),'%d-%m-%Y %H:%i:%s') AS cdate
FROM chats AS c LEFT OUTER JOIN
messages AS m ON c.ChatID = m.ChatID
WHERE c.Accepted = 1 AND (c.firstPerson=59 OR c.SecondPerson=59)
GROUP BY c.chatID, c.firstPerson, c.secondPerson
ORDER BY cdate DESC
I'm not sure if in MySql applying the str_to_date function to an aggregate field works, in the negative case the query should be rewritten as:
SELECT chatID,
firstPerson,
secondPerson,
str_to_date(cdate,'%d-%m-%Y %H:%i:%s') AS cdate
FROM (SELECT c.chatID,
c.firstPerson,
c.secondPerson,
MAX(COALESCE(m.creationDate, c.creationDate)) AS cdate
FROM chats AS c LEFT OUTER JOIN
messages AS m ON c.ChatID = m.ChatID
WHERE c.Accepted = 1 AND (c.firstPerson=59 OR c.SecondPerson=59)
GROUP BY c.chatID, c.firstPerson, c.secondPerson
ORDER BY cdate DESC) AS devTbl
I hope this help

MySQL query: select records with highest value in group

I have a MySQL table like this.
| season_id | round_1 | names | score_round_1
| 5 | 10 | John1 | 5
| 5 | 10 | John2 | 3
| 5 | 11 | John3 | 2
| 5 | 11 | John4 | 5
I want to select the records with highest score_round_1 in each round_1(10,11) group .
In this case the first and last rows would be selected.
I tried using the GROUP BY round_1 but that only returns the first row from the two.
Any advice?
Zolka
This is simple
select max(score_round_1),
name
from score
group by round_1
SELECT *
FROM table p1
WHERE score_round_1 = (
SELECT MAX( p2.score_round_1 )
FROM table p2
WHERE p1.round_1 = p2.round_1 ) ANDround_1 !=0
Use aggregate function MAX
SELECT names, MAX(score_round_1) GROUP BY round_1