4

I'm totally new with SQL. I have two tables that I connect with an inner join, sorted, and filtered. Then I need to show only the two most recent entries grouped by "username." I don't know how to do that. I've looked at some of the solutions posted, but I don't know how to apply those to my situation.

Here's where I am now:

SELECT users.username, logs.event, logs.insertdate
FROM users 
INNER JOIN logs ON users.identifier = logs.useridentifier
WHERE (((logs.event)="Taken" Or (logs.event)="Return"))
ORDER BY users.username, logs.insertdate DESC;

Here's an example of the output I get:

|username | event | insertdate | 
================================
 alan     | Return| 10/15/2014 3:17 PM
 alan     | Taken | 10/15/2014 6:49 AM
 alan     | Return| 10/14/2014 3:16 PM
 alan     | Taken | 10/14/2014 6:50 AM
 alan     | Return| 10/13/2014 3:15 PM
 bill     | Return| 10/15/2014 3:19 PM
 bill     | Taken | 10/15/2014 6:53 AM
 bill     | Return| 10/14/2014 3:26 PM
 bill     | Taken | 10/14/2014 7:00 AM
 bill     | Return| 10/13/2014 3:19 PM

and so on. What I'm hoping to do is get just the top two entries from each user. So

|username | event | insertdate | 
================================
 alan     | Return| 10/15/2014 3:17 PM
 alan     | Taken | 10/15/2014 6:49 AM
 bill     | Return| 10/15/2014 3:19 PM
 bill     | Taken | 10/15/2014 6:53 AM

Thanks

Glenn
  • 8,932
  • 2
  • 41
  • 54
Barry
  • 43
  • 3

1 Answers1

3

use the "analytic" function row_number()

SELECT
      username
    , event
    , insertdate
FROM (
            SELECT
                  users.username
                , logs.event
                , logs.insertdate
                , ROW_NUMBER() OVER (PARTITION BY users.username
                                     ORDER BY logs.insertdate DESC) AS rn
            FROM users
                  INNER JOIN logs
                              ON users.identifier = logs.useridentifier
            WHERE (logs.event = "Taken"
                  OR logs.event = "Return")
      ) AS derived
WHERE rn < 3
ORDER BY
      username, insertdate DESC;

This variant might be worth trying to see if it helps performance (or not):

SELECT
      users.username
    , logrn.event
    , logrn.insertdate
FROM users
      INNER JOIN (
                  SELECT
                        logs.useridentifier
                      , logs.event
                      , logs.insertdate
                      , ROW_NUMBER() OVER (PARTITION BY logs.useridentifier
                                           ORDER BY logs.insertdate DESC) AS rn
                  FROM logs
                  WHERE (logs.event = "Taken" OR logs.event = "Return")
            ) AS logrn
                  ON users.identifier = logrn.useridentifier
                        AND rn < 3
ORDER BY
      users.username, logrn.insertdate DESC
;
Paul Maxwell
  • 33,002
  • 3
  • 32
  • 51
  • I recommend row_number(). rank() or dense_rank() can produce the same number for multiple rows, hence if you want to ensure there is only 2 rows, don't use ranking functions. – Paul Maxwell Oct 16 '14 at 00:21