【问题标题】:Query for the latest message in each conversation with every other user查询与每个其他用户的每次对话中的最新消息
【发布时间】:2015-04-16 15:01:57
【问题描述】:

我有一张表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";

但此查询需要获取对话中的所有消息!我只需要最后一条消息。

【问题讨论】:

  • SELECT * FROM messages WHERE fromUser='1' AND forUser='2' ORDER BY submitDate DESC?
  • 我需要获取用户 1 的所有对话的最后一条消息。
  • 到目前为止您尝试过什么吗?为什么 submitDate 是 5 个字符的字符串?
  • 我提出了我的问题。它只是样本数据。但真正的是时间戳。

标签: 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)

【讨论】:

    【解决方案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 的最新消息。

    【讨论】:

    • tnx 贝索斯先生。我用我在 mysql 和 fiddle 站点中的真实数据尝试了你建议的查询。但结果不一样!在小提琴中是可以的,但在 mysql 中它有错误的答案。你能看看这个吗:sqlfiddle.com/#!9/9f790/1非常感谢。
    • @Saeid 在这个小提琴演示中,用户 1 向自己发送了一条消息。结果似乎还可以。每个对话的最新消息已返回。
    【解决方案3】:

    如果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
    

    【讨论】:

    • tnx 约翰·布林格。是的 messageID 是 auto_increment 。但此查询仅返回“$myUserID”的最后一次对话中的最后一条消息。我的意思是它在该示例数据中返回 messageID -> 7。我需要它返回:messageID:用户 1 的 4,6,7。
    • @Saeid,好的,我已经根据您的说明修改了答案以满足要求。
    【解决方案4】:

    事实证明,我有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 切换。

    【讨论】:

      猜你喜欢
      • 2015-05-12
      • 2018-11-15
      • 1970-01-01
      • 2016-05-09
      • 2014-01-18
      • 2021-03-05
      • 1970-01-01
      • 2016-09-10
      • 1970-01-01
      相关资源
      最近更新 更多