如何从DB中选择重叠值对

时间:2011-01-25 11:00:42

标签: sql postgresql

在我的postgres db中,我有一个包含3列的表格,如下所示:

start | end  | sorce
17    | 23   | A
150   | 188  | A
200   | 260  | A
19    | 30   | B
105   | 149  | B
199   | 220  | B
...

我想选择来自不同来源A和B的区域(从头到尾)重叠的所有行。

更新

从postgres版本8.4可以用window functions来解决问题。它比连接或子选择方法快得多。 Link to postgres wiki

2 个答案:

答案 0 :(得分:2)

这可以作为一种强力方法(我重命名你的列range_start和range_end以避免与保留字“end”发生冲突):

select *
from t cross join t t2
where t2.source <> t.source
      and box(point(t2.range_start,t2.range_start),point(t2.range_end,t2.range_end))
            && box(point(t.range_start,t.range_start),point(t.range_end,t.range_end))

select *
from t
where exists (select 1 from t t2
              where t2.source <> t.source and box(point(t2.range_start,t2.range_start),point(t2.range_end,t2.range_end))
                  && box(point(t.range_start,t.range_start),point(t.range_end,t.range_end)))

然后,您应该能够使用GiST索引,这可以使其更有效(seq扫描+索引扫描):

create index t_range_idx on t using gist (box(point(range_start,range_start),point(range_end,range_end))

此功能可能通过清除SQL来帮助理解:

create function range(not_before int, not_after int) returns box
   strict immutable language sql
   as $$ select box(point($1,$1),point($2,$2)) $$;

有了这个,你可以写:

select * from t where range(range_start,range_end) && range(10,20);

并注意box && box运算符表示“重叠”。

答案 1 :(得分:0)

如果你想要所有配对,那么使用经典重叠测试INNER JOIN A和B

a.start < b.end and b.start < a.end

select a.start a_start, a.end a_end, b.start b_start, b.end b_end
from tbl a
inner join tbl b on a.start < b.end and b.start < a.end and b.source = 'B'
where a.source = 'A'

如果您不是指来源literally'A'和'B',只是它们不同,您可以使用以下代替

select a.start a_start, a.end a_end, b.start b_start, b.end b_end
from tbl a
inner join tbl b on a.start < b.end and b.start < a.end and a.source <> b.source

根据您对重叠的定义,将<<=交换(两次)

  • <:10-20 重叠20-30
  • <=:10-20 重叠20-30