64

My table looks like this (and I'm using MySQL):

m_id | v_id | timestamp
------------------------
6    |   1  | 1333635317
34   |   1  | 1333635323
34   |   1  | 1333635336
6    |   1  | 1333635343
6    |   1  | 1333635349

My target is to take each m_id one time, and order by the highest timestamp.

The result should be:

m_id | v_id | timestamp
------------------------
6    |   1  | 1333635349
34   |   1  | 1333635336

And i wrote this query:

SELECT * FROM table GROUP BY m_id ORDER BY timestamp DESC

But, the results are:

m_id | v_id | timestamp
------------------------
34   |   1  | 1333635323
6    |   1  | 1333635317

I think it causes because it first does GROUP_BY and then ORDER the results.

Any ideas? Thank you.

Rick
  • 7,007
  • 2
  • 49
  • 79
Luis
  • 3,257
  • 13
  • 50
  • 59
  • 5
    use `MAX` to select the max from your group – Nanne Apr 05 '12 at 14:45
  • 11
    A `GROUP BY` clause with no aggregate function (eg: `COUNT(), SUM(), MAX()`) makes no sense at all. It baffles me that MySQL even allows this. Think about it, why are you grouping if you don't do anything with the groups? – NullUserException Apr 05 '12 at 14:47
  • What if there are two different v_id for an m_id? What would the desired result be? – Joachim Isaksson Apr 05 '12 at 14:49
  • 2
    Ordering contents of "groups" was a bug in previous versions of MySQL. As of SQL standarts, in this case `ORDER BY` must influence only results of `GROUP BY`, not data before grouping. – Timur Apr 05 '12 at 14:59
  • 6
    @NullUserException - The *correct* use for `GROUP BY` without an aggregate function is to group by the primary key. Useful where your joins yield 1:many `SELECT a.id, a.name, a.age, MAX(b.savings) FROM a INNER JOIN b on a.id = b._id GROUP BY a.id` – MatBailie Apr 05 '12 at 15:35
  • 2
    @NullUserException It allows for fallbacks when doing a many-to-many join with a lookup table. – Izkata Oct 23 '13 at 15:29

8 Answers8

71

One way to do this that correctly uses group by:

select l.* 
from table l
inner join (
  select 
    m_id, max(timestamp) as latest 
  from table 
  group by m_id
) r
  on l.timestamp = r.latest and l.m_id = r.m_id
order by timestamp desc

How this works:

  • selects the latest timestamp for each distinct m_id in the subquery
  • only selects rows from table that match a row from the subquery (this operation -- where a join is performed, but no columns are selected from the second table, it's just used as a filter -- is known as a "semijoin" in case you were curious)
  • orders the rows
Matt Fenwick
  • 48,199
  • 22
  • 128
  • 192
  • Thank you very much its working great. Can you explain me what is this the 'r'? it's a temp table? how does it call in SQL? – Luis Apr 05 '12 at 15:22
  • 2
    @luis - Imagine that the sub-query is a view; an in-line view. It needs a name so you can reference it and it's fields. This answer give the in-line view the name `r`. You could call it almost anything you like. – MatBailie Apr 05 '12 at 15:31
  • 2
    @Luis it's an alias by which you can refer to the subquery. I could also have written `(select ...) as r` (note the as) to make it more clear. – Matt Fenwick Apr 05 '12 at 15:33
  • It works perfect but the inner join statement is missing a FROM clause – babooney Oct 18 '12 at 08:34
  • @babooney thanks, nice catch! It was supposed to be `table`, though, not `l`. – Matt Fenwick Oct 18 '12 at 13:03
  • 1
    thanks @MattFenwick it works for me and you saved my time – Janarthanan Ramu Jun 10 '21 at 17:59
18

If you really don't care about which timestamp you'll get and your v_id is always the same for a given m_i you can do the following:

select m_id, v_id, max(timestamp) from table
group by m_id, v_id
order by max(timestamp) desc

Now, if the v_id changes for a given m_id then you should do the following

select t1.* from table t1
left join table t2 on t1.m_id = t2.m_id and t1.timestamp < t2.timestamp
where t2.timestamp is null
order by t1.timestamp desc
Mosty Mostacho
  • 42,742
  • 16
  • 96
  • 123
8

Here is the simplest solution

select m_id,v_id,max(timestamp) from table group by m_id;

Group by m_id but get max of timestamp for each m_id.

Pang
  • 9,564
  • 146
  • 81
  • 122
abinash sahoo
  • 167
  • 1
  • 5
  • I don't think you'll get the correct row everytime. Please verify all fields of the row – Niranjan Shetty Oct 13 '22 at 12:09
  • 1
    This answer is almost correct. You should include each field that is not part of an aggregate clause in the group by. So if you edit your answer to be "group by m_id, v_id" then this should be the accepted answer. – axiopisty Mar 30 '23 at 17:47
5

You can try this

 SELECT tbl.* FROM (SELECT * FROM table ORDER BY timestamp DESC) as tbl
 GROUP BY tbl.m_id  
samayo
  • 16,163
  • 12
  • 91
  • 106
Ronak Patel
  • 3,324
  • 4
  • 21
  • 31
4

SQL>

SELECT interview.qtrcode QTR, interview.companyname "Company Name", interview.division Division 
FROM interview 
JOIN jobsdev.employer 
    ON (interview.companyname = employer.companyname AND employer.zipcode like '100%')
GROUP BY interview.qtrcode, interview.companyname, interview.division
ORDER BY interview.qtrcode;
Lee Han Kyeol
  • 2,371
  • 2
  • 29
  • 44
Go ahead
  • 41
  • 2
2

I felt confused when I tried to understand the question and answers at first. I spent some time reading and I would like to make a summary.

  1. The OP's example is a little bit misleading. At first I didn't understand why the accepted answer is the accepted answer.. I thought that the OP's request could be simply fulfilled with
select m_id, v_id, max(timestamp) as max_time from table
group by m_id, v_id
order by max_time desc

Then I took a second look at the accepted answer. And I found that actually the OP wants to express that, for a sample table like:

m_id | v_id | timestamp
------------------------
6    |   1  | 11
34   |   2  | 12
34   |   3  | 13
6    |   4  | 14
6    |   5  | 15

he wants to select all columns based only on (group by)m_id and (order by)timestamp.

Then the above sql won't work. If you still don't get it, imagine you have more columns than m_id | v_id | timestamp, e.g m_id | v_id | timestamp| columnA | columnB |column C| .... With group by, you can only select those "group by" columns and aggreate functions in the result.

By far, you should have understood the accepted answer. What's more, check row_number function introduced in MySQL 8.0:

https://www.mysqltutorial.org/mysql-window-functions/mysql-row_number-function/

  1. Finding top N rows of every group

It does the simlar thing as the accepted answer.

  1. Some answers are wrong. My MySQL gives me error.
select m_id,v_id,max(timestamp) from table group by m_id;

@abinash sahoo

SELECT m_id,v_id,MAX(TIMESTAMP) AS TIME
 FROM table_name 
 GROUP BY m_id

@Vikas Garhwal

Error message:

[42000][1055] Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'testdb.test_table.v_id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
Rick
  • 7,007
  • 2
  • 49
  • 79
-2

Why make it so complicated? This worked.

SELECT m_id,v_id,MAX(TIMESTAMP) AS TIME
 FROM table_name 
 GROUP BY m_id
Pang
  • 9,564
  • 146
  • 81
  • 122
-3

Just you need to desc with asc. Write the query like below. It will return the values in ascending order.

SELECT * FROM table GROUP BY m_id ORDER BY m_id asc;
Pang
  • 9,564
  • 146
  • 81
  • 122
Gurpreet Singh
  • 165
  • 4
  • 18
  • 3
    That won't work. While it will reverse the results, it still won't return the desired results. – Richlv Aug 03 '17 at 20:32