SQL select only rows with max value on a column [duplicate] SQL select only rows with max value on a column [duplicate] mysql mysql

SQL select only rows with max value on a column [duplicate]


At first glance...

All you need is a GROUP BY clause with the MAX aggregate function:

SELECT id, MAX(rev)FROM YourTableGROUP BY id

It's never that simple, is it?

I just noticed you need the content column as well.

This is a very common question in SQL: find the whole data for the row with some max value in a column per some group identifier. I heard that a lot during my career. Actually, it was one the questions I answered in my current job's technical interview.

It is, actually, so common that StackOverflow community has created a single tag just to deal with questions like that: .

Basically, you have two approaches to solve that problem:

Joining with simple group-identifier, max-value-in-group Sub-query

In this approach, you first find the group-identifier, max-value-in-group (already solved above) in a sub-query. Then you join your table to the sub-query with equality on both group-identifier and max-value-in-group:

SELECT a.id, a.rev, a.contentsFROM YourTable aINNER JOIN (    SELECT id, MAX(rev) rev    FROM YourTable    GROUP BY id) b ON a.id = b.id AND a.rev = b.rev

Left Joining with self, tweaking join conditions and filters

In this approach, you left join the table with itself. Equality goes in the group-identifier. Then, 2 smart moves:

  1. The second join condition is having left side value less than right value
  2. When you do step 1, the row(s) that actually have the max value will have NULL in the right side (it's a LEFT JOIN, remember?). Then, we filter the joined result, showing only the rows where the right side is NULL.

So you end up with:

SELECT a.*FROM YourTable aLEFT OUTER JOIN YourTable b    ON a.id = b.id AND a.rev < b.revWHERE b.id IS NULL;

Conclusion

Both approaches bring the exact same result.

If you have two rows with max-value-in-group for group-identifier, both rows will be in the result in both approaches.

Both approaches are SQL ANSI compatible, thus, will work with your favorite RDBMS, regardless of its "flavor".

Both approaches are also performance friendly, however your mileage may vary (RDBMS, DB Structure, Indexes, etc.). So when you pick one approach over the other, benchmark. And make sure you pick the one which make most of sense to you.


My preference is to use as little code as possible...

You can do it using INtry this:

SELECT * FROM t1 WHERE (id,rev) IN ( SELECT id, MAX(rev)  FROM t1  GROUP BY id)

to my mind it is less complicated... easier to read and maintain.


I am flabbergasted that no answer offered SQL window function solution:

SELECT a.id, a.rev, a.contents  FROM (SELECT id, rev, contents,               ROW_NUMBER() OVER (PARTITION BY id ORDER BY rev DESC) rank          FROM YourTable) a WHERE a.rank = 1 

Added in SQL standard ANSI/ISO Standard SQL:2003 and later extended with ANSI/ISO Standard SQL:2008, window (or windowing) functions are available with all major vendors now. There are more types of rank functions available to deal with a tie issue: RANK, DENSE_RANK, PERSENT_RANK.