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。你不希望那种连接来解决这个问题。
我有一个 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。你不希望那种连接来解决这个问题。