views:

61

answers:

2

I know how to get the top values but am having trouble with something very simple.

I have a student table. It has:

  • name
  • numberoflaps
  • grade

I want the get a query or report that shows the top two kids with the most laps per grade.

+1  A: 

SQL Server 2005+ version would look like this:

;WITH Laps_CTE AS
(
    SELECT
        grade, name, numberoflaps,
        ROW_NUMBER() OVER (
            PARTITION BY grade
            ORDER BY numberoflaps DESC
        ) AS RowNum
    FROM students
)
SELECT grade, name, numberoflaps
FROM Laps_CTE
WHERE RowNum <= 2

If that's not your dialect, please let us know what is.

Aaronaught
I think you should have WHERE RowNum < 3 to return the top 2 rows
Martin Booth
Sorry, I spent so long writing that comment Aaronaught had already fixed it!
Martin Booth
@Martin Booth: Got to be quick around here :)
OMG Ponies
+3  A: 

Using MySQL:


MySQL doesn't have any ranking functionality, but it does allow for variable creation & updating:

SELECT x.grade,
       x.name,
       x.numberoflaps
  FROM (SELECT s.grade,
               s.name,
               s.numberoflaps,
               CASE 
                 WHEN @grade != s.grade THEN @rownum := 1 
                 ELSE @rownum := @rownum + 1
               END AS rank,
               @grade := s.grade
          FROM STUDENTS s,
               (SELECT @rownum := 0, @grade := NULL) r
      ORDER BY s.grade, s.numberoflaps DESC) x
 WHERE x.rank <= 2
ORDER BY x.grade, x.rank

The ORDER BY in the subquery is important, otherwise the ranking won't be properly performed.

Using Oracle 9i+/SQL Server 2005+:


Using a CTE:

WITH laps AS (
  SELECT s.grade, 
         s.name, 
         s.numberoflaps,
         ROW_NUMBER() OVER (PARTITION BY grade ORDER BY numberoflaps DESC) AS rank
    FROM STUDENTS s)
  SELECT l.grade, 
         l.name, 
         l.numberoflaps
    FROM laps l
   WHERE l.rank <= 2
ORDER BY l.grade, l.numberoflaps DESC

Non-CTE equivalent:

  SELECT l.grade, 
         l.name, 
         l.numberoflaps
    FROM (SELECT s.grade, 
                 s.name, 
                 s.numberoflaps,
                 ROW_NUMBER() OVER (PARTITION BY grade ORDER BY numberoflaps DESC) AS rank
            FROM STUDENTS s) l
   WHERE l.rank <= 2
ORDER BY l.grade, l.numberoflaps DESC

Caveat:

Oracle gained ranking functionality in 9i; for SQL Server it was 2005.

OMG Ponies