2014-03-29 167 views
3

我有这个表的独特组合messages查找所有记录有两列

sender_id recipient_id 
1   2 
1   3 
1   3 
2   1 

我要选择的记录,从而使:

  1. 无论SENDER_ID或receiver_id = current_user.id
  2. 的其他领域应该是唯一的

即只有以下记录有在上表中选择

sender_id recipient_id 
1   2 
1   3 

为什么?因为我希望构建一个类似Facebook的收件箱,其中发送和接收的邮件被汇总,而这个查询是迄今为止的瓶颈。

我使用导轨3.2和Postgres 9.3

回答

4
SELECT DISTINCT sender_id, recipient_id 
FROM messages 
WHERE $current_user_id IN (sender_id, receiver_id) 
AND sender_id <= receiver_id; 

最后一个条件是必要的折(2,1)和(1,2)转换成单排。

如果,违背了你的榜样,也可以在你的数据(2,1)没有(1,2)也是在那里,它变得更加复杂。一个UNION查询应该是完美的:

WITH cte AS (
    SELECT sender_id, recipient_id 
    FROM messages 
    WHERE $current_user_id IN (sender_id, receiver_id) 
    ) 
SELECT sender_id, recipient_id FROM cte 
WHERE sender_id <= receiver_id 
UNION 
SELECT recipient_id, sender_id FROM cte 
WHERE sender_id > receiver_id; 

CTE应该更快通过保持它归结为一个单一的索引扫描,而不是两个。

UNION从结果中删除重复项,使得DISTINCT步不必要。
对于排序后的输出,您可能希望在末尾添加ORDER BY子句。

您需要两列的索引才能获得最佳性能。虽然个别指标可以用良好的表现bitmax索引扫描相结合,在(sender_id, receiver_id)一个multicolumn index仍然会更快:

CREATE INDEX foo_idx ON messages (sender_id, recipient_id); 

与往常一样,权衡成本和效益的指标。如果查询是瓶颈,那么索引可能是个好主意。

+0

哇...只是 –

+0

完美。 http://sqlfiddle.com/#!15/5a39c/5/0 –

+0

@Erwin我如何订购这些结果以获得最新的第一个?该表还有一个id列和一个created_at列,两者都可以用于排序...但是如何? –

2

随着ANSI SQL:

SELECT DISTINCT sender_id, reciepient_id 
FROM messages 
WHERE (sender_id = current_user.id or reciepient_id = current_user.id) 
+0

如果您在'sender_id'和'receiver_id'上有独立的索引(* not *两者都是复合索引),PostgreSQL应该使用位图索引扫描来查找匹配的行。 –

+1

这无法删除重复项。 –

+0

是的,这不会删除(1,2)vs(2,1)重复 –

相关问题