MySQL 日期的最大值未返回正确的元组

MySQL Max of a Date not returning the correct tuple

我有一个 table“消息”,用于存储随着时间的推移发送给人们的关于某些项目的消息。

消息的结构 table 是:

message_id user_id date_sent created_at

对于每个用户,我可以在 table 中有多个元组。 这些消息有的已经发送了,有的还没有发送。

我正在尝试为每个用户获取最后创建的消息。 我正在使用 max(created_at) 和一个 group_by(user_id),但是关联的 message_id 不是与 max(created_id) 关联的那个元组。

Table数据:

   message_id | user_id | date_sent | created_at
----------------------------------------------
    1           1       2021-07-01  2021-07-01        
    2           1       2021-07-02  2021-07-02
    3           2       2021-07-01  2021-07-01
    4           3       2021-07-04  2021-07-04
    5           1       2021-07-22  2021-07-22
    6           1       NULL        2021-07-23
    7           2       NULL        2021-07-29
    8           1       NULL        2021-07-29
    9           3       2021-07-29  2021-07-29

我的Select:

select * from messages ma right join 
( SELECT max(mb.created_at), message_id
FROM `messages` mb WHERE mb.created_at <= '2021-07-24' 
group by user_id)
mc on ma.message_id=mc.message_id

结果是

   message_id | user_id | date_sent | created_at
----------------------------------------------      
    5           1       2021-07-22  2021-07-23
    3           2       2021-07-01  2021-07-01
    4           3       2021-07-04  2021-07-04

我不知道为什么,但是对于用户 1,返回的 message_id 不是与具有 max(created_at).

的元组关联的那个

我期待的是:(获取 select 的最大值(date_sent)的元组,按 user_id 分组)

   message_id | user_id | date_sent | created_at
----------------------------------------------      
    6           1       NULL        2021-07-23
    3           2       2021-07-01  2021-07-01
    4           3       2021-07-04  2021-07-04

有什么想法吗?有什么帮助吗? 谢谢。

您正在为 MySQL 的 notorious nonstandard extension to GROUP BY 绊倒。它给你一种错觉,你可以做你做不到的事情。范例

 SELECT max(created_at), message_id
   FROM messages 
  GROUP BY user_id

其实就是

 SELECT max(created_at), ANY_VALUE(message_id)
   FROM messages 
  GROUP BY user_id

其中 ANY_VALUE() 表示 MySQL 可以从该用户的消息中选择任何 message_id 它认为最方便的消息。这不是你想要的。

要解决您的问题,您需要先使用子查询为每个 user_id 查找最新的 created_at 日期。 Fiddle.

              SELECT user_id, MAX(created_at) created_at
                FROM messages
               WHERE created_at <= '2021-07-24'
               GROUP BY user_id

然后,您需要找到在该日期创建的特定 user_id 的消息。为此使用子查询。 Fiddle

  SELECT a.*
    FROM messages a
    JOIN (
              SELECT user_id, MAX(created_at) created_at
                FROM messages
               WHERE created_at <= '2021-07-24'
               GROUP BY user_id
         ) b ON a.user_id = b.user_id AND a.created_at = b.created_at

看到 JOIN 是如何工作的了吗?它会提取与每个用户的最新日期匹配的行。

有一个可能的优化。如果

  • 您的 message_id 是一个自动递增的主键并且
  • 您从不更新 created_at 列,而只是在插入行时将它们设置为当前日期

那么每个 user_id 的最新消息也是最大 message_id 的消息。在这种情况下,您可以改用此查询。 Fiddle

  SELECT a.*
    FROM messages a
    JOIN (
              SELECT user_id, MAX(message_id) message_id
                FROM messages
               WHERE created_at <= '2021-07-24'
               GROUP BY user_id
         ) b ON a.message_id=b.message_id

由于主键索引的工作方式,这可能会更快。

您需要普通的 JOIN 而不是 RIGHT 或 LEFT JOIN:普通的 JOIN 只有 returns 行符合 ON 条件。

专业提示 几乎没有人实际使用 RIGHT JOIN。当您想要那种 JOIN 时,请使用 LEFT JOIN。你不希望那种连接来解决这个问题。