81

I want an SQL statement to get the row with a minimum value.

Consider this table:

id  game   point
1    x      5
1    z      4
2    y      6
3    x      2
3    y      5
3    z      8

How do I select the ids that have the minimum value in the point column, grouped by game? Like the following:

id  game   point    
1    z      4
2    y      5
3    x      2   
Amira Bedhiafi
  • 8,088
  • 6
  • 24
  • 60
balaji
  • 1,236
  • 2
  • 18
  • 28

11 Answers11

82

Use:

SELECT tbl.*
FROM TableName tbl
  INNER JOIN
  (
    SELECT Id, MIN(Point) MinPoint
    FROM TableName
    GROUP BY Id
  ) tbl1
  ON tbl1.id = tbl.id
WHERE tbl1.MinPoint = tbl.Point
Ken Clark
  • 2,500
  • 15
  • 15
  • Can a single inner join be done when writing in an SQL in Microsoft access?? – KSM Apr 11 '14 at 16:53
  • 6
    unneccessary complecation SELECT id , game , MIN(point) FROM tablename GROUP BY id,game – Sourav Sarkar May 09 '14 at 16:12
  • 12
    @SouravSarkar, that will not work That example will return every row from the original query, since it is looking for the lowest point from every id *and* game. (e.g. the lowest score where id = 1 and game = x is 5; the lowest score where id = 1 and game = z is 4) – RPh_Coder Apr 08 '15 at 16:18
  • I agree. This is unnecessary. Using Window functions is a much cleaner approach and works irrespective of the schema, also reduces the need for a join. – Shiroy May 19 '16 at 23:43
  • As mentioned above, it does not work when there are two rows with same id, and min value like [id, game, point] [1, x, 4][1, y, 4] – Pouya BCD Jun 10 '16 at 19:24
  • wow i love this shorthand way of creating a table of values inplace - can you do this in MySQL? i just tried and got errors.... – bhu Boue vidya Sep 25 '17 at 05:33
41

This is another way of doing the same thing, which would allow you to do interesting things like select the top 5 winning games, etc.

 SELECT *
 FROM
 (
     SELECT ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Point) as RowNum, *
     FROM Table
 ) X 
 WHERE RowNum = 1

You can now correctly get the actual row that was identified as the one with the lowest score and you can modify the ordering function to use multiple criteria, such as "Show me the earliest game which had the smallest score", etc.

Shiroy
  • 1,648
  • 1
  • 15
  • 22
23

This will work

select * from table 
where (id,point) IN (select id,min(point) from table group by id);
Eric Leschinski
  • 146,994
  • 96
  • 417
  • 335
Aspirant
  • 2,238
  • 9
  • 31
  • 43
19

As this is tagged with sql only, the following is using ANSI SQL and a window function:

select id, game, point
from (
  select id, game, point, 
         row_number() over (partition by game order by point) as rn
  from games
) t
where rn = 1;
1

Ken Clark's answer didn't work in my case. It might not work in yours either. If not, try this:

SELECT * 
from table T

INNER JOIN
  (
  select id, MIN(point) MinPoint
  from table T
  group by AccountId
  ) NewT on T.id = NewT.id and T.point = NewT.MinPoint

ORDER BY game desc
Community
  • 1
  • 1
samthebrand
  • 3,020
  • 7
  • 41
  • 56
1
SELECT DISTINCT 
FIRST_VALUE(ID) OVER (Partition by Game ORDER BY Point) AS ID,
Game,
FIRST_VALUE(Point) OVER (Partition by Game ORDER BY Point) AS Point
FROM #T
0
SELECT * from room
INNER JOIN
  (
  select DISTINCT hotelNo, MIN(price) MinPrice
  from room
 Group by hotelNo
  ) NewT   
 on room.hotelNo = NewT.hotelNo and room.price = NewT.MinPrice;
josliber
  • 43,891
  • 12
  • 98
  • 133
  • 3
    A short explanation as to how this code answers the question would greatly improve this answer – jmoreno Sep 27 '15 at 04:12
0

This alternative approach uses SQL Server's OUTER APPLY clause. This way, it

  1. creates the distinct list of games, and
  2. fetches and outputs the record with the lowest point number for that game.

The OUTER APPLY clause can be imagined as a LEFT JOIN, but with the advantage that you can use values of the main query as parameters in the subquery (here: game).

SELECT colMinPointID
FROM (
  SELECT game
  FROM table
  GROUP BY game
) As rstOuter
OUTER APPLY (
  SELECT TOP 1 id As colMinPointID
  FROM table As rstInner
  WHERE rstInner.game = rstOuter.game
  ORDER BY points
) AS rstMinPoints
The Conspiracy
  • 3,495
  • 1
  • 17
  • 18
0

This is portable - at least between ORACLE and PostgreSQL:

select t.* from table t 
where not exists(select 1 from table ti where ti.attr > t.attr);
thenor
  • 1
  • 1
  • 1
    How is this supposed to answer the question? – Mike Dec 16 '20 at 13:46
  • The query selects any record for which there is no other record in the same table with a larger attribute value. If multiple rows have the same minimal attribute value, they are all returned.``` select t.* from table t where not exists(select 1 from table ti where ti.attr > t.attr); ``` – thenor Dec 16 '20 at 14:52
  • Even if your answer is right, by using random table and column names you're making it difficult to see the relation between your answer and the question. – Mike Dec 18 '20 at 14:44
0

Most of the answers use an inner query. I am wondering why the following isn't suggested.

select
   *
from
   table
order by
   point
fetch next 1 row only   // ... or the appropriate syntax for the particular DB

This query is very simple to write with JPAQueryFactory (a Java Query DSL class).

return new JPAQueryFactory(manager).
   selectFrom(QTable.table).
   setLockMode(LockModeType.OPTIMISTIC).
   orderBy(QTable.table.point.asc()).
   fetchFirst();
Nathan
  • 8,093
  • 8
  • 50
  • 76
-4

Try:

select id, game, min(point) from t
group by id 
www
  • 4,365
  • 1
  • 23
  • 24
  • 1
    i'm getting this error "Column 'student.point' is invalid in the HAVING clause because it is not contained in either an aggregate function or the GROUP BY clause. " – balaji Mar 08 '13 at 10:35
  • 2
    This will not give desired result. Because you need to group your query with id, game, point to remove error and that will return all records. – Ken Clark Mar 08 '13 at 10:57
  • Sorry but I dont understand. What is wrong here: http://sqlfiddle.com/#!2/8b9c7/8/0 – www Mar 08 '13 at 11:01
  • the code would be : select id, game, min(point) from tablename group by id,game – Sourav Sarkar May 09 '14 at 16:08
  • @Wawrzyniec - the problem is that your result is incorrect. ID 1, game z, min(point) 4 should be correct. – alexplanation Nov 26 '14 at 20:05