2015-04-16 31 views
3

對於用戶之間的對話,我有一個表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

'SELECT * FROM消息WHERE FROMUSER = '1' AND forUser = '2' ORDER BY submitDate DESC'? –

+0

我需要爲用戶1獲取所有對話的最後一條消息。 – Saeid

+1

您到目前爲止嘗試過的任何內容?爲什麼submitDate是一個5個字符的字符串? –

回答

2

如果messageIdauto_increment主鍵,那麼您可以使用它的值來區分哪個是每個對話中的最新消息。如果submitDate的類型爲DATETIMETIMESTAMP那麼這將是另一種選擇,但如果它的類型爲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 
+0

tnx約翰布林傑。是messageID是auto_increment。但是這個查詢只返回'$ myUserID'的最後一個對話中的最後一條消息。我的意思是它返回messageID - > 7在該示例數據。我需要它return:messageID:用戶1的4,6,7。 – Saeid

+0

@Saeid,好吧,我已經修改了我的答案,以符合要求根據您的澄清。 –

+0

工程就像一個魅力!謝謝 – Filippo

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 

SQL Fiddle Demo

內查詢:

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最近的消息。

+0

tnx Betsos先生。我嘗試了你的建議查詢與我的真實數據在MySQL和小提琴網站。但結果不同!在小提琴是好的,但在MySQL中它有錯誤的答案。你可以看看這個:http://sqlfiddle.com/#!9/9f790/1非常感謝。 – Saeid

+0

@Saeid在這個小提琴演示中,用戶1發送了一條消息給自己。結果似乎沒問題。每個會話的最近消息已被返回。 –

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,也支持窗口查詢。考慮從MySQL切換,如果你正在做任何嚴重涉及SQL的事情。

1

試試這個簡單而輕鬆的一年,也將找到各組看的用戶在我的代碼: -

select m.* ,u.* 
    from 
     messages m 
     inner join (
      select max(id) as maxid 
      from messages 
      where messages.fromUser = "$myUsreId" 
      OR messages.forUser = "$myUsreId"    
      group By (if(fromUser > forUser, fromUser, forUser)), 
      (if(fromUser > forUser, forUser, fromUser)) 
      ) t1 on m.id=t1.maxid 
     join 
     users u ON u.id = (CASE WHEN m.fromUser = "$myUsreId" 
          THEN m.forUser 
          ELSE m.fromUser   
         END) 
相關問題