我有一个表messages
用于用户之间的对话。
表格列的名称是:
messageID | fromUser | forUser | message | submitDate | seen
示例数据:
1 | 1 | 2 | "hi" | "12341" | 0
2 | 2 | 1 | "hi" | "12342" | 0
3 | 1 | 3 | "hi" | "12343" | 0
4 | 1 | 4 | "hi 4" | "12344" | 0
5 | 2 | 1 | "hello" | "12345" | 0
6 | 1 | 2 | "hello how r u?" | "12346" | 0
7 | 3 | 1 | "hello user 1" | "12345" | 0
如何编写查询以查找自己与系统中其他所有用户之间发送的最后一条消息? 我的意思是最后的消息是:
between user 1 and 2 : "hello how r u?"
between user 1 and 3 : "hello user 1"
between user 4 and 1 : "hi 4""
我的查询:
$query = "SELECT DISTINCT `fromUser`, `forUser`, `message`, `seen`,
`username`, `userPhoto`
FROM `messages`,`user`
WHERE (`forUser`= '$myUserID' OR `fromUser`= '$myUserID')
AND (((`forUser`= `userID`) AND (`forUser` != '$myUserID'))
OR ((`fromUser`= `userID`)
AND (`fromUser` != '$myUserID')))
ORDER BY `submitDate` DESC";
但是此查询需要获取对话中的所有消息!我只需要最后一条消息。
答案 0 :(得分:2)
如果messageId
是auto_increment
主键,则可以使用其值来区分每个会话中的最新消息。如果submitDate
的类型为DATETIME
或TIMESTAMP
,那么它将是另一种选择,但如果它的类型为DATE
,则其分辨率不足。
但关键是识别和过滤最新消息的时间戳或ID。您可以使用合适的聚合(子)查询在每个会话的基础上识别ID或时间戳,并通过内部联接执行过滤,如下所示:
SELECT m.*
FROM
messages m
JOIN (
SELECT
MAX(messageId),
CASE
WHEN fromUser = '$myUserId' THEN forUser
WHEN forUser = '$myUserId' THEN fromUser
END AS otherUser
FROM messages
GROUP BY
CASE
WHEN fromUser = '$myUserId' THEN forUser
WHEN forUser = '$myUserId' THEN fromUser
END
HAVING otherUser IS NOT NULL
) other
ON m.messageId = other.messageId
答案 1 :(得分:2)
您可以使用以下查询为每位用户与其他用户进行userID = 1
的用户获取最新消息:
SELECT messageID, message, submitDate, otherUser
FROM (
SELECT messageID, message, submitDate,
@row_number:=CASE WHEN @other=otherUser THEN @row_number+1
ELSE 1
END AS row_number,
@other:=otherUser AS otherUser
FROM (
SELECT messageID, message, submitDate,
IF(1 = fromUser, forUser, fromUser) as otherUser
FROM messages AS m
WHERE 1 IN (fromUser,forUser)
ORDER BY otherUser, submitDate DESC) t ) s
WHERE s.row_number = 1
内部查询:
SELECT messageID, message, submitDate,
IF(1 = fromUser, forUser, fromUser) as otherUser
FROM messages AS m
WHERE 1 IN (fromUser,forUser)
ORDER BY otherUser, submitDate DESC
用于返回用户userID = 1
发送或接收的消息列表。计算列otherUser
只包含与userID = 1
用户对话中涉及的其他用户发件人或收件人。
在外部查询中,使用变量@row_number
来模拟SQL Server中可用的ROW_NUMBER() OVER (PARTITION BY otherUser ORDER BY submitDate DESC)
。
最后,外部查询使用@row_number
计算的数字来仅选择otherUser
的最新邮件。
答案 2 :(得分:1)
尝试这个简单易用的方法,它也会找到每个组的用户查看我的代码: -
{{1}}
答案 3 :(得分:0)
事实证明,我已经answered a nearly identical question on Code Review。
SELECT DISTINCT
通常表明你做错了什么。几乎总是可以在不需要DISTINCT
的情况下制定更好的查询。
一个有用的技巧是在对话者和自我方面重新标记发件人和收件人。然后,过滤结果只是一个简单的问题。仅保留self
是相关用户的邮件。然后,具有相同interlocutor
的每一行在概念上构成一个线程。
SELECT fromUser, forUser, message, seen, otherUser.username, otherUser.userPhoto
FROM
( -- Messages I sent
SELECT *, forUser AS interlocutor, fromUser AS self
FROM messages
UNION
-- Messages I received
SELECT *, fromUser AS interlocutor, forUser AS self
FROM messages
) AS thread_latest
INNER JOIN users AS otherUser
ON otherUser.userID = interlocutor
WHERE
self = '$myUserID' AND
-- Discard all but the latest message in each thread
NOT EXISTS (
SELECT messageID
(
SELECT *, forUser AS interlocutor, fromUser AS self
FROM messages
UNION
SELECT *, fromUser AS interlocutor, forUser AS self
FROM messages
) AS thread_later
WHERE
thread_later.self = thread_latest.self AND
thread_later.interlocutor = thread_latest.interlocutor AND
thread_later.submitDate > thread_latest.submitDate
)
ORDER BY submitDate DESC;
请注意,有一个子查询出现两次。我们可以通过创建视图来使其更清晰。
CREATE VIEW threads AS
-- Messages I sent
SELECT *, forUser AS interlocutor, fromUser AS self
FROM messages
UNION
-- Messages I received
SELECT *, fromUser AS interlocutor, forUser AS self
FROM messages;
SELECT fromUser, forUser, message, seen, otherUser.username, otherUser.userPhoto
FROM
threads AS thread_latest
INNER JOIN users AS otherUser
ON otherUser.userID = interlocutor
WHERE
self = '$myUserID' AND
-- Discard all but the latest message in each thread
NOT EXISTS (
SELECT messageid
FROM threads AS thread_later
WHERE
thread_later.self = thread_latest.self AND
thread_later.interlocutor = thread_latest.interlocutor AND
thread_later.time > thread_latest.time
)
ORDER BY submitDate DESC;
我将借此机会指出这个查询是PostgreSQL真正发挥作用的地方。 PostgreSQL中的两个功能(自8.4版本起)使其变得简单。 WITH
clause允许您在查询本身中定义帮助器视图。更重要的是,
window functions让你通过对话者对线程进行分区,这正是这个问题的棘手部分。
WITH threads(messageID, fromUser, forUser, message, submitDate, interlocutor, self) AS (
-- Messages I sent
SELECT *, forUser, fromUser FROM messages
UNION
-- Messages I received
SELECT *, fromUser, forUser FROM messages
), myThreads AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY interlocutor ORDER BY submitDate DESC) AS thread_pos
FROM threads
WHERE self = $myUserId
)
SELECT fromUser, forUser, message, seen, otherUser.username, otherUser.userPhoto
FROM my_threads
INNER JOIN users AS otherUser
ON otherUser.userID = interlocutor
WHERE thread_pos = 1 -- Only the latest message per thread
ORDER BY submitDate DESC;
其他数据库(如MS SQL Server和Oracle)也支持窗口查询。如果你正在做任何涉及SQL的事情,请考虑从MySQL切换。