根据两个条件选择最近的记录

时间:2015-02-19 13:15:01

标签: mysql sql

我有user1与user2和user4交换消息(这些参数是已知的)。我现在想为每个对话选择最新发送或接收的消息(即每个对话的LIMIT 1)。

SQLFiddle

目前,我的查询会返回所有会话的所有消息:

SELECT * 
FROM message 
WHERE (toUserID IN (2,4) AND userID = 1)
OR    (userID IN (2,4) AND toUserID = 1)
ORDER BY message.time DESC

返回的行应该是messageID 3和6。

4 个答案:

答案 0 :(得分:1)

您可以将ORDER BYLIMIT分成两个查询,然后将其与UNION加在一起,轻松完成此操作:

(SELECT *
FROM message 
WHERE (toUserID IN (2,4) AND userID = 1)
ORDER BY message.time DESC
LIMIT 1)
UNION
(SELECT * 
FROM message 
WHERE (userID IN (2,4) AND toUserID = 1)
ORDER BY message.time DESC
LIMIT 1)

括号在这里很重要,这会返回消息2和6,这看起来是正确的,而不是3和6。

您似乎可以使用UNION ALL代替UNION,因为两个查询之间不会有重复,但如果您决定这样做会更好。

这是您的数据:

MESSAGEID  USERID   TOUSERID    MESSAGE           TIME
1          1        2           nachricht 1       123
2          1        2           nachricht 2       124
3          2        1           nachricht 3       125
4          3        2           nachricht wrong   1263
5          2        4           nachricht wrong   1261
6          4        1           nachricht sandra  126

答案 1 :(得分:1)

假设更高的id值表示更新的消息,您可以这样做:

  • 查找涉及用户1的所有邮件
  • 按其他用户ID
  • 对结果进行分组
  • 获取每组的最大邮件ID
SELECT *
FROM message
WHERE messageID IN (
    SELECT MAX(messageID)
    FROM message
    WHERE userID = 1 -- optionally filter by the other user
    OR toUserID = 1 -- optionally filter by the other user
    GROUP BY CASE WHEN userID = 1 THEN toUserID ELSE userID END
)
ORDER BY messageID DESC

Updated SQLFiddle

答案 2 :(得分:1)

以下按要求运作:

SELECT  m1.*
FROM    Message m1
        LEFT JOIN Message m2
            ON LEAST(m1.toUserID, m1.userID) = LEAST(m2.toUserID, m2.userID)
            AND GREATEST(m1.toUserID, m1.userID) = GREATEST(m2.toUserID, m2.userID)
            AND m2.time > m1.Time
WHERE   m2.MessageID IS NULL
AND (   (m1.toUserID IN (2,4) AND m1.userID = 1)
    OR  (m1.userID IN (2,4) AND m1.toUserID = 1)
    );

为了简化其工作原理,想象一下你只想要用户ID 1发送的最新消息,而不是必须匹配to / from元组,因为这会给查询增加无用的混乱。要得到这个,我会使用:

SELECT  m1.*
FROM    Message AS m1
        LEFT JOIN Message AS m2
            ON m2.UserID = m1.UserID
            AND m2.time > m1.time
WHERE   m1.UserID = 1
AND     m2.MessageID IS NULL;

因此,我们正在加入类似的消息,规定第二条消息(m2)的时间比第一条消息的时间长,其中m2为空,这意味着以后的时间没有类似的消息,因此m2是最新的消息

在解决方案中已经应用了主体,但是我们有一个更复杂的连接来链接对话。

我在联接中使用了LEASTGREATEST,理论是因为你的元组(UserID, ToUserID)中有2个成员,所以在任何组合中,最大和最小的将是同样的,例如:

From/To |  Greatest | Least |
--------+-----------+-------+
  1, 2  |     2     |   1   |
  2, 1  |     2     |   1   |
  1, 4  |     4     |   1   |
  4, 1  |     4     |   1   |
  4, 2  |     4     |   2   |
  2, 4  |     4     |   2   |

正如您所看到的,在类似的From / To中,最大和最小的将是相同的,因此您可以使用它将表连接到自身。

答案 3 :(得分:1)

您的查询有两部分 以下顺序

  1. 您希望在两个用户之间进行对话的最新传出或传入消息
  2. 您希望为两对不同的用户提供这些最新消息,即对话。
  3. 因此,让我们获取UserID a和UserID b之间对话的最新消息:

    SELECT *
    FROM message 
    WHERE (toUserID, userID) IN ((a, b), (b, a)) 
    ORDER BY message.time DESC
    LIMIT 1
    

    然后,您希望将这些组合用于UserID 1和2以及UserID 1和4之间的两个对话。这是联盟发挥作用的地方(我们不需要检查重复项,因此我们使用UNION ALL,谢谢马库斯亚当斯,他首先提出了这个问题。)

    所以完整而直接的解决方案将是:

    (SELECT *
    FROM message 
    WHERE (toUserID, userID) IN ((2, 1), (1, 2)) 
    ORDER BY message.time DESC
    LIMIT 1)
    UNION ALL
    (SELECT * 
    FROM message 
    WHERE (toUserID, userID) IN ((4, 1), (1, 4)) 
    ORDER BY message.time DESC
    LIMIT 1)
    

    正如预期的那样,您会在SQLFiddle中收到消息3和6。