【问题标题】:How to get the latest message in each conversation of a certain user in SQL?如何在 SQL 中获取某个用户的每个对话中的最新消息?
【发布时间】:2014-01-18 08:02:09
【问题描述】:

我需要编写一个查询来返回两个用户之间对话中的最新消息。我在这个小提琴中包含了架构和我的(失败的)尝试:http://sqlfiddle.com/#!15/322c3/11

我已经解决这个问题一段时间了,但每次我运行任何丑陋的查询时一只可爱的小猫死了

任何帮助将不胜感激。为小猫做这件事。

【问题讨论】:

  • +1 为工作小提琴。
  • 小猫怎么了?

标签: sql postgresql greatest-n-per-group


【解决方案1】:

...两个用户之间的对话中的最新消息。

假设 ID 为 13 的用户,就像你在小提琴中所做的那样,我们对最新的 created_at(sender_id, receiver_id)(1,3)(3,1) 的消息感兴趣。

您可以使用 ad-hoc 行类型来缩短语法:

SELECT * 
FROM   messages 
WHERE  (sender_id, receiver_id) IN ((1,3), (3,1))
ORDER  BY created_at DESC
LIMIT  1;

或显式(并且速度稍快,也更容易与索引一起使用):

SELECT * 
FROM   messages 
WHERE (sender_id = 1 AND receiver_id = 3 OR
       sender_id = 3 AND receiver_id = 1)
ORDER  BY created_at DESC
LIMIT  1;

对于用户的所有个会话

根据评论中的要求添加了解决方案。

SELECT DISTINCT ON (user_id) *
FROM (
   SELECT 'out' AS type, id, receiver_id AS user_id, body, created_at
   FROM   messages 
   WHERE  sender_id = 1

   UNION  ALL
   SELECT 'in' AS type, id, sender_id AS user_id, body, created_at
   FROM   messages 
   WHERE  receiver_id = 1
   ) sub
ORDER  BY user_id, created_at DESC;

这里的做法是将外来的sender/receiver折叠成一列,以简化最后一行的提取。

在此相关答案中对DISTINCT ON的详细解释:
Select first row in each GROUP BY group?

-> Updated SQLfiddle.

还要考虑小提琴中改进和简化的测试用例。

【讨论】:

  • 我喜欢这个解决方案。看起来非常优雅。但是,如果我想列出特定用户的所有对话的所有最新消息怎么办?也就是说,每一行代表被查询用户的不同对话。
  • 您确定您的查询的两个版本会生成不同的执行计划吗?我希望优化器可以看到它们是相同的并生成相同的计划(包括索引使用)。至少对于来自 SQLFiddle 的微小数据集,这些计划是 100% 相同的。
  • @Wittek:我为此添加了一个解决方案。
  • @Erwin 这不只是我的解决方案吗?
  • @a_horse_with_no_name:过去我看到逐行比较有点慢。但也许从那以后这已经得到了补救?也许 OP 可以在他的大表(带索引?)上使用 EXPLAIN ANALYZE 对两个查询中的每一个进行快速测试并报告回来?
【解决方案2】:

这提供了两个用户之间的最新消息,无论消息方向如何:

  SELECT Distinct mes.ID, sendu.Username AS Sender,
  recu.Username as Receiver, Body, maxSent as TimeSent

  FROM messages mes

  INNER JOIN 

  (
    SELECT One, Two, MAX(CREATED_AT) maxSent
    FROM
    (
      SELECT  'Sender' as type, Sender_ID AS One, receiver_id as Two,created_At
      FROM messages 

      UNION ALL

      SELECT 'Receiver' as type, receiver_id AS One, Sender_ID as Two ,created_At
      FROM messages 
    ) a

    Group By One,Two
  ) b

  ON mes.created_at = b.maxSent

  INNER JOIN users sendu
  ON sendu.ID = mes.Sender_ID

  INNER JOIN users recu
  ON recu.ID = mes.Receiver_ID

它没有将“对话”分开,但没有任何意义。也许如果您还包含消息标题或标题字段,这将是可能的。

【讨论】:

    【解决方案3】:
    SELECT * 
    FROM messages 
    WHERE (sender_id = 1 AND receiver_id = 2) 
       OR (sender_id = 2 AND receiver_id = 1) 
    ORDER BY created_at DESC
    LIMIT 1;
    

    【讨论】:

    • 合并与所有用户的所有对话,而不仅仅是与特定用户的对话。
    【解决方案4】:

    试试这个看看

    SELECT  *
    FROM    (SELECT * ,ROW_NUMBER() OVER(ORDER BY created_at DESC) as RowID
    FROM   messages 
    WHERE  (sender_id, receiver_id) IN ((1,3), (3,1))
        ) sub
    WHERE   RowID = 1
    

    【讨论】:

    • 这只会获取最新消息,而不是如问题所述,两个用户之间对话中的最新消息。 where子句中什么都没有,你没有选择任何列,Postrgesql使用LIMIT而不是TOP
    • 加号:至少应该是top 1,而不是top1
    【解决方案5】:

    use this --> select * from messages order by created_at desc limit 1

    【讨论】:

    • 根本不回答问题。
    猜你喜欢
    • 2015-05-12
    • 1970-01-01
    • 2016-11-02
    • 1970-01-01
    • 2020-12-25
    • 1970-01-01
    • 2021-03-18
    • 1970-01-01
    • 2017-05-13
    相关资源
    最近更新 更多