事實證明,我已經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,也支持窗口查詢。考慮從MySQL切換,如果你正在做任何嚴重涉及SQL的事情。
'SELECT * FROM消息WHERE FROMUSER = '1' AND forUser = '2' ORDER BY submitDate DESC'? –
我需要爲用戶1獲取所有對話的最後一條消息。 – Saeid
您到目前爲止嘗試過的任何內容?爲什麼submitDate是一個5個字符的字符串? –