我有这个SQL表Messages
,其中相关条目为sender
,recipient
,timestamp
和text
。例如,一些例子是
sender recipient text timestamp
user1 user2 hey 1
user2 user1 hello back 2
user1 user3 hey other dude 3
例如,为了填充iMessage
中的视图;我需要一个SQL查询,以便给定用户在用户拥有的每个对话中获取最新消息。所以目前我所拥有的是
SELECT *
FROM Messages m
WHERE m.timestamp IN (SELECT MAX(m2.timestamp)
FROM Messages m2
WHERE m.sender = :user OR m.recipient = :user
GROUP BY sender, recipient)
但遗憾的是,此查询返回上述所有3条消息,因为嵌套选择将前两个分别分组,即使它们都在同一个对话中。有没有一种简单的方法来表达我在SQL中真正想要的东西,最好不要创建一个Conversations表并做一些有趣的业务?
答案 0 :(得分:0)
在外部查询中,您尝试将“m.id
”与内部查询进行比较,而不是使用“m.id
”,您应该使用“m.timestamp
”
的
的SELECT *
FROM Messages m
WHERE m.timestamp IN (SELECT max(m2.timestamp)
FROM Messages m2
WHERE m.sender = :user
OR m.recipient = :user)
的
答案 1 :(得分:0)
你的方法基本上是行不通的。现在你正在检查显然不起作用的WHERE m.id IN (SELECT MAX(m2.timestamp)
。
假设这是一个拼写错误,并且您打算检查WHERE m.timestamp IN ...
它仍然不可靠,因为来自不同会话的多条消息可能具有相同的时间戳。
相反,让我们放弃那种方法,因为它充满了困难,而只是执行anti-join
。
select m.*
from messages m
left join messages m2
on ((m.sender = m2.sender and m.recipient = m2.recipient)
or (m.sender = m2.recipient and m.recipient = m2.sender))
and m.timestamp < m2.timestamp
where m2.sender is null
通过加入两个条件,我们基本上加入'对话',无论发送者和接收者是否相同,或者一个的发送者是另一个的接收者。我们的最终谓词有一个时间范围 - 当不存在具有更高时间戳的消息时,外连接将返回空值,因此我们过滤where
子句中的那些(这是反-join )
答案 2 :(得分:-1)
如果您想查找用户的上一条消息,请尝试以下操作:
SELECT *
FROM `Messages`
WHERE `recipient` = :user OR `sender` = :user
ORDER BY `timestamp` DESC
LIMIT 1
如果您想在两个用户之间找到最后一条消息,请尝试以下方法:
SELECT *
FROM `Messages`
WHERE
:user1 + :user2 =
CASE WHEN `recipient` = :user1 THEN `recipient`
WHEN `sender` = :user1 THEN `sender`
ELSE NULL
END +
CASE WHEN `recipient` = :user2 THEN `recipient`
WHEN `sender` = :user2 THEN `sender`
ELSE NULL
END
ORDER BY `timestamp` DESC
LIMIT 1