聊天系统消息查询:如何获取用户间最新交互记录并去重
解决聊天系统消息列表重复对话问题
我明白你遇到的问题了——用DISTINCT *确实解决不了重复对话的问题,因为每条消息本身都是唯一的,所以你会看到同一个用户来回的所有消息。咱们来优化这个查询,拿到每个对话的最新交互记录,就像Facebook或Twitter的消息列表那样:
首先,核心思路是:把两个用户之间的所有消息归为一个“对话”,然后只保留每个对话里的最新一条消息。假设你的message表包含id(消息ID)、userFrom、userTo、content、created_at(消息发送时间)字段(如果没有created_at,用自增的id也可以,默认ID越大消息越新)。
方法1:子查询+关联表
这种方法先找出每个对话的最新消息时间,再关联原表拿到对应消息:
SELECT m.* FROM message m INNER JOIN ( -- 第一步:按对话分组,找到每个对话的最新消息时间 SELECT LEAST(userFrom, userTo) AS user1, -- 把较小的用户ID放前面 GREATEST(userFrom, userTo) AS user2, -- 较大的放后面,确保对话唯一 MAX(created_at) AS latest_time FROM message WHERE userFrom = 2 OR userTo = 2 -- 过滤当前用户的所有消息 GROUP BY user1, user2 ) AS latest ON -- 匹配对话的两种情况:当前用户是发件人/收件人 ((m.userFrom = latest.user1 AND m.userTo = latest.user2) OR (m.userFrom = latest.user2 AND m.userTo = latest.user1)) AND m.created_at = latest.latest_time WHERE m.userFrom = 2 OR m.userTo = 2 ORDER BY m.created_at DESC; -- 按最新消息时间倒序,和社交平台一致
方法2:窗口函数(更简洁高效)
用ROW_NUMBER()窗口函数给每个对话的消息按时间排序,只取第一条:
SELECT * FROM ( SELECT *, -- 按对话分组,每组内按消息时间倒序排名 ROW_NUMBER() OVER ( PARTITION BY LEAST(userFrom, userTo), GREATEST(userFrom, userTo) ORDER BY created_at DESC, id DESC -- 时间相同则取ID大的消息 ) AS rn FROM message WHERE userFrom = 2 OR userTo = 2 ) AS ranked WHERE rn = 1 -- 只保留每个对话的第一条(最新)消息 ORDER BY created_at DESC;
注意事项
- 如果你的表没有
created_at字段,可以把MAX(created_at)换成MAX(id),窗口函数里的ORDER BY created_at DESC换成ORDER BY id DESC(前提是ID是自增的,越大表示消息越新)。 - 如果存在同一时间两条消息的情况,方法2里的
ORDER BY created_at DESC, id DESC可以确保取到最新插入的那条。 - 最终结果按
created_at DESC排序,能让最新有交互的对话排在最顶部,符合用户使用习惯。
内容的提问来源于stack exchange,提问作者shady




