0

I have two tables:

User (id, name)
UserEvent (id, user_id, name, date)

How can I get all the users where the last (ordered by date) UserEvent.name has a value of 'played'?

I wrote an example on SQLFiddle with some specific data: http://sqlfiddle.com/#!9/b76e24 - For this scenario I would just get 'Mery' from table User, because even though 'John' has associated events name of the last one is not 'played'.

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
Hommer Smith
  • 26,772
  • 56
  • 167
  • 296
  • is it correct that the id in UserEvent is always 1 and not auto increment ? – Bernd Buffen Oct 12 '15 at 22:22
  • Edited, sorry. It's an autoincrement. – Hommer Smith Oct 12 '15 at 22:23
  • Your fiddle is for MySQL? But you are still talking about Postgres, right? A table name `User` is impossible without double quoting. Data types don't make sense for Postgres, either. Please provide a valid, consistent question. And, as always, your version of Postgres, please. – Erwin Brandstetter Oct 13 '15 at 03:08

3 Answers3

1

This is probably fastest:

SELECT u.*
FROM   usr u  -- avoiding "User" as table name
JOIN   LATERAL (
   SELECT name
   FROM   userevent
   WHERE  user_id = u.id
   ORDER  BY date DESC NULLS LAST
   LIMIT  1
   ) ue ON ue.name = 'played';

LATERAL requires Postgres 9.3+:

Or you could use DISTINCT ON (faster for few rows per user):

SELECT u.*
FROM   usr u  -- avoiding "User" as table name
JOIN   (
   SELECT DISTINCT ON (user_id)
          user_id, name
   FROM   userevent
   ORDER  BY user_id, date DESC NULLS LAST
   ) ue ON ue.user_id = u.id
       AND ue.name = 'played';

Details for DISTINCT ON:

SQL Fiddle with valid test case.

If date is defined NOT NULL, you don't need NULLS LAST. (Neither in the index below.)

Key to read performance for both but especially the first query is a matching multicolumn index:

CREATE INDEX userevent_foo_idx ON userevent (user_id, date DESC NULLS LAST, name);

Aside: Never use reserved words as identifiers.

Community
  • 1
  • 1
Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
0

Return the max date from user event grouping by user id. Take that result set and join it back to user event by the user id and max date and filter for just the played records.

Rabbit
  • 507
  • 3
  • 9
0

Here it is:

First i get the MAX ID from each user and join it to the ROW with this ID to test if the status are 'played' if so, i get the username of them.

SELECT
  ids.*,
  u.name,
  ue.*
FROM (
  SELECt max(id) AS id from UserEvent
  GROUP by  user_id
) as ids
LEFT JOIN UserEvent ue ON ue.id = ids.id
LEFT JOIN User u ON u.id  = ue.user_id
WHERE ue.name = 'played';
Bernd Buffen
  • 14,525
  • 2
  • 24
  • 39
  • I voted down because the query won't return the desired result which you should have noticed when you tested it. See http://sqlfiddle.com/#!9/b76e24/8 At least not with the sample fiddle provided in the question. It might be correct under other assumptions though – jpw Oct 12 '15 at 23:01
  • You must use the corrected query AND change your data. UserEvent id is always 1, so it cant work with the same ids http://sqlfiddle.com/#!9/e324b – Bernd Buffen Oct 12 '15 at 23:09
  • i removed the dw, but note that with the fiddle you linked to the query still won't return the right data. – jpw Oct 12 '15 at 23:15
  • Sorry, for thois sample is the result correct. (1, 1, 'finish', '2015-08-10'), (2, 1, 'played', '2015-08-9'), (3, 2, 'played', '2015-08-9') first user 1 (id1) the user finished and in the next id (2) the user 1 played. if you change the ids to 2,1,3 will will give your wanted result – Bernd Buffen Oct 12 '15 at 23:21