基于JOIN在SQL中获取GROUP BY的最新结果

时间:2014-11-07 01:10:03

标签: sql postgresql greatest-n-per-group

我有两个SQL表usersmessages,如下所示:

user_id | username
--------|---------
      1 | alice
      2 | bob
      3 | carol

message_id | sending_user_id | message | created_utc
-----------|-----------------|---------|-------------
          1|               1 | a       | 67
          2|               1 | b       | 68
          3|               3 | c       | 69
          4|               2 | d       | 70
          5|               3 | e       | 71
          6|               1 | f       | 72

我正在尝试编写一个SQL查询,该查询将导致messageuser次{1}}所订购的每个created_utc的最新message。所以结果应该是这样的:

message_id | sending_user_id | message | created_utc | sending_username
-----------|-----------------|---------|-------------|----------------------
          6|               1 | f       | 72          | alice
          5|               3 | e       | 71          | carol
          4|               2 | d       | 70          | bob

我坚持如何使用以下查询保证邮件始终是最新的:

SELECT messages.message_id, messages.sending_user_id, messages.message, messages.created_utc, users.username AS sending_username
FROM messages 
LEFT JOIN ON users 
WHERE messages.sending_user_id=users.user_id 
GROUP BY messages.sending_user_id
ORDER BY messages.created_utc DESC

编辑:我正在使用PostgreSQL 9.3.5

2 个答案:

答案 0 :(得分:3)

对于Postgres,我建议distinct on

SELECT DISTINCT ON (m.sending_user_id) m.message_id, m.sending_user_id, m.message, m.created_utc,
       u.username AS sending_username
FROM messages m LEFT JOIN
     users u
     on m.sending_user_id = u.user_id 
ORDER BY m.sending_user_id, m.created_utc DESC;

如果您想要最终order by,请使用子查询:

SELECT um.*
FROM (SELECT DISTINCT ON (m.sending_user_id) m.message_id, m.sending_user_id, m.message, m.created_utc,
              u.username AS sending_username
      FROM messages m LEFT JOIN
           users u
           on m.sending_user_id = u.user_id 
      ORDER BY m.sending_user_id, m.created_utc DESC
     ) um
ORDER BY created_utc;

答案 1 :(得分:0)

我认为最好(也是最灵活)的ANSI SQL解决方案是NOT EXISTS

SELECT    m1.message_id, m1.sending_user_id, m1.message, m1.created_utc, users.username AS sending_username
FROM      messages m1 
LEFT JOIN users ON (m1.sending_user_id=users.user_id)
WHERE     NOT EXISTS (
            SELECT *
            FROM   messages m2
            WHERE  m2.sending_user_id = m1.sending_user_id
            AND    m2.created_utc < m1.created_utc
          )