Postgresql收件箱查询

Postgresql收件箱查询,sql,postgresql,Sql,Postgresql,我有一个消息表,如下所示: Messages +-----+------------+-------------+--------------+ | id | sender_id | receiver_id | created_at | +-----------------------------------------------+ | 1 | 1 | 2 | 1/1/2013 | | 2 |

我有一个消息表,如下所示:

                    Messages
+-----+------------+-------------+--------------+
|  id |  sender_id | receiver_id |  created_at  |
+-----------------------------------------------+
|  1  |      1     |      2      |   1/1/2013   |
|  2  |      1     |      2      |   1/1/2013   |
|  3  |      2     |      1      |   1/2/2013   |
|  4  |      3     |      2      |   1/2/2013   |
|  5  |      3     |      2      |   1/3/2013   |
|  6  |      5     |      4      |   1/4/2013   |
+-----------------------------------------------+
如果“线程”是给定发送者id和接收者id之间的一组消息,我希望查询返回最近10个线程的最近10条消息,其中发送者id或接收者id是给定id

给定用户id为5时的预期输出:

+-----+------------+-------------+--------------+
|  id |  sender_id | receiver_id |  created_at  |
+-----------------------------------------------+
|  1  |      5     |      2      |   1/4/2013   |
|  2  |      5     |      2      |   1/4/2013   |
|  3  |      2     |      5      |   1/4/2013   |
|  4  |      3     |      5      |   1/4/2013   |
|  5  |      5     |      2      |   1/3/2013   |
|  6  |      5     |      4      |   1/3/2013   |
+-----------------------------------------------+
例如,在用户5和用户2之间,最多10条消息有4条,在用户5和用户2之间,最多10条线程有3条

我一直在尝试使用子查询进行这类查询,但没有设法获得不同线程数的第二个限制

SELECT * FROM (SELECT DISTINCT ON (sender_id, receiver_id) messages.* 
FROM messages 
WHERE (receiver_id = 5 OR sender_id = 5) ORDER BY sender_id, receiver_id, 
created_at DESC)   
q ORDER BY created_at DESC 
LIMIT 10 OFFSET 0;

我正在考虑创建一个新的线程表,其中包含一个线程id字段,该字段将是发送者id+接收者id的串联,然后只是加入消息,但我有一个隐秘的怀疑,认为它应该只需要一个表就可以实现。

我发布这个帖子是为了展示可以做什么

我真的不推荐使用它

最好执行两个单独的查询:1个检索最近的10个线程,1个重复检索每个线程最近的10条消息

但是,您可以通过如下所示的排名实现您的目标

select * from (
      select message.*,
             rank() over (partition by message.sender, message.receiver 
                              order by sent desc )  
      from sof_messages message,
           (
            select sender, 
                   receiver,
                   max(sent) 
              from sof_messages
             where receiver = <user>
                or sender = <user>
             group by sender,
                   receiver
             order by 3
             limit 10
           ) thread
      where message.sender = thread.sender
        and message.receiver = thread.receiver
      ) message_list

where rank <= 10
有两种不同的查询可以通过窗口函数实现您的目标,但没有一种查询特别干净。

由于数据重复,创建线程表看起来是错误的,但视图可能会有帮助:

CREATE VIEW threads AS 
  SELECT sender_id, receiver_id, min(created_at) AS t_date
  FROM messages
  GROUP BY sender_id,receiver_id;
如果线程的日期是其最新消息的日期,而不是最旧消息的日期,则将mincreated_at更改为maxcreated_at

然后,它可以通过以下方式连接回消息:

SELECT ... FROM messages JOIN threads USING (sender_id,receiver_id)

我能想象的在一个查询中解决您的问题的最整洁的查询如下:

select * from (
  select row_number() 
    over (partition by sender_id, receiver_id order by created_at desc) as rn, m.*
  from Messages m
  where (m.sender_id, m.receiver_id) in (
    select sender_id, receiver_id
    from Messages
    where sender_id = <id> or receiver_id = <id>
    group by sender_id, receiver_id
    order by max(created_at) desc
    limit 10 offset 0
  )
) res where res.rn <= 10

“按发件人id划分的分区上的行号”、“按发件人id划分的分区上的行号”、“按已创建的分区上的接收方id排序”列将包含每个线程中每条消息的行号。如果您运行单独的查询以仅查询一个线程,则它将与记录号类似。除此行号外,如果消息包含在由…query…中的m.sender\u id、m.receiver\u id生成的最上面的10个线程中,则可以查询消息本身。。。。最后,由于您只需要10条最上面的消息,因此将行号限制为小于或等于10。

我建议采用couling的答案并稍微修改一下,这样它就可以使用一个公共表表达式有效地提供两个查询:

WITH threads (sender_id, receiver_id, latest) as (
        select sender, 
               receiver,
               max(sent) 
          from sof_messages
         where receiver = <user>
            or sender = <user>
         group by sender,
               receiver
         order by 3
         limit 10
 ), 
 messages ([messages fields listed here], rank) as (
         select m.*, 
                rank() over (partition by (sender, receiver), order by sent desc)
           from sof_messages
          WHERE (sender, receiver) in (select (sender, receiver) from threads))
 SELECT * from messages where rank <= 10;

这样做的好处是,规划者可以很好地知道何时在这里使用索引。本质上,查询的三个部分中的每一部分都是独立规划的。

我还没有对此进行测试,但您似乎忘记了子查询上的限制10,它提供了10个最新线程:

SELECT
  *
FROM
  (SELECT DISTINCT ON
     (sender_id, receiver_id) messages.* 
   FROM
     messages 
   WHERE
     (receiver_id = 5 OR sender_id = 5)
   ORDER BY
     sender_id, receiver_id, created_at DESC
   LIMIT
     10)   
  q
ORDER BY
  created_at DESC 
LIMIT
  10
OFFSET
  0;

我已经很好地打印了SQL,因此更容易判断发生了什么。

您可以根据上面显示的示例数据显示您尝试过的查询和预期的输出吗?只是要澄清一下,您是要求每个线程返回最多10行的1条消息,还是要求每个线程返回最多100行的10条消息。@couling我要求的是10条消息每个线程最多返回100条消息rows@johnnymire好的,我已经修改了我的答案来适应这个。不过,正如回答中所述,我认为最好将其分解为多个查询。@johnnymire:最近的线程假设线程的一条消息给出了线程的日期。您应该指定它是最早的线程开始消息,还是最新的线程更新消息。