在我的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
答案 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