2013-02-05 28 views
6

我有一個消息表看起來像這樣:PostgreSQL的收件箱中查詢

    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 | 
+-----------------------------------------------+ 

凡有「線」是一個給定的SENDER_ID和receiver_id我想查詢返回最近10之間的一組消息最近10個線程的消息其中sender_id或receiver_id是給定的ID。

預期輸出,其中給定的user_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 | 
+-----------------------------------------------+ 

高達之間,10條消息的限制,例如,用戶5和2(上面有4)和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; 

我正在考慮創建包含的thread_id場這將是SENDER_ID + receiver_id然後剛剛加盟的消息的串聯一個新的線程表,但我有一個鬼鬼祟祟的懷疑,它應該是可行的只有一個表。

+0

您可以根據上面顯示的示例數據顯示您嘗試過的查詢和預期的輸出嗎? – bonCodigo

+0

要明確你要求每個線程1條消息(最多返回10行)或每條線程10條消息(最多返回100行)。 –

+0

@couling我要求每個線程10條消息(最多返回100行) – johnnymire

回答

0

我沒有測試過這一點,但它看起來像你忘了你的子查詢的LIMIT 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因此它更容易分辨正在發生什麼。)

1

我發佈這個顯示可以做什麼。

我不推薦使用它。

做兩個單獨的查詢會好得多:1檢索10個最新的線程,1個重複地爲每個線程撤回10條最新的消息。

但是,您可以使用rank()window function實現您的目標,如下所示。

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 

有幾個不同的查詢,將與窗口的功能,他們沒有特別的乾淨實現自己的目標。

1

創建Thread表看起來錯誤的,因爲數據重複的,但鑑於可能會有幫助:

CREATE VIEW threads AS 
    SELECT sender_id, receiver_id, min(created_at) AS t_date 
    FROM messages 
    GROUP BY sender_id,receiver_id; 

變化min(created_at)max(created_at)如果線程的日期是其最近消息的日期,而不是最老的一個。

然後,它可以連接回消息簡單地:

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

我能想象到一個查詢中解決您的問題,最整潔的查詢下列之一:

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 

row_number() over (partition by sender_id, receiver_id order by created_at desc)列將包含每個線程內的每條消息的行號(如果您運行單獨的查詢以僅查詢一個線程,它將與記錄號相似)。除了這個行號之外,如果消息本身包含在10個最上面的線程中(這是由(m.sender_id, m.receiver_id) in ...query...生成的),那麼查詢消息本身。最後,因爲只需要10個最上面的消息,您將行數限制爲低於或等於10。

2

我會建議服用庫壽齡的答案,稍微修改它,使它不使用公用表表達式提供有效的兩個查詢:

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; 

這樣具有允許規劃者有一個相當不錯的主意的優勢何時在這裏使用索引。實質上,查詢的三個部分中的每一個都是獨立計劃的。