数据样本:
id lowerlimt upperlimit
1 5 10 ---Master Record
2 8 12
3 3 8
4 8 9
5 11 15
在上表中,让我们假设以id=1
为主记录的记录。我希望将其他记录与同一表中的第一条记录进行比较,如果它与范围重叠并分配标记,则拆分每条记录。如果重叠分配'Y'
,则为'N'
。
如果它重叠,则将记录部分拆分为两个,一个用于重叠范围,另一个用于非重叠范围。
id lowerlimt upperlimit flag
2 8 10 y
2 10 12 n
3 3 5 n
3 5 8 y
4 8 9 y
5 11 15 n
答案 0 :(得分:1)
我添加了更多"测试数据"用于测试和插图。主要的计算是将输入范围分解为最多三个(其中一些没有意义并且在最后阶段被消除 - 当输入范围严格地与主记录重叠时,达到最多三个在两个方向)。
为了提高效率,最好只访问每个输入行一次。因此,我不是同时使用union all
(最简单的路由),而是同时创建所有三个子范围和相应的标志,结果有九列而不是三列用于子范围和标志。然后我使用unpivot
将它们分成不同的行。
with
test_data ( id, lowerlimit, upperlimit ) as (
select 1, 5, 10 from dual union all ---Master Record
select 2, 8, 12 from dual union all
select 3, 3, 8 from dual union all
select 4, 8, 9 from dual union all
select 5, 11, 15 from dual union all
select 6, 2, 5 from dual union all
select 7, 1, 14 from dual
)
-- end of test data (not part of the solution)
-- SQL query begins BELOW THIS LINE (use your actual table name)
select id, lowerlimit, upperlimit, flag
from (
select id,
t.lowerlimit as x1, least(t.upperlimit, m.ll) as y1, 'n' as f1,
greatest(t.lowerlimit, m.ll) as x2, least(t.upperlimit, m.ul) as y2, 'y' as f2,
greatest(t.lowerlimit, m.ul) as x3, t.upperlimit as y3, 'n' as f3
from test_data t cross join
( select lowerlimit ll, upperlimit ul
from test_data
where id = 1
) m
where t.id != 1
)
unpivot ( ( lowerlimit, upperlimit, flag )
for ( x, y, f ) in ( ( x1, y1, f1), (x2, y2, f2), (x3, y3, f3) ) )
where lowerlimit < upperlimit
order by id, lowerlimit -- if needed
;
<强>输出强>:
ID LOWERLIMIT UPPERLIMIT FLAG
-- ---------- ---------- ----
2 8 10 y
2 10 12 n
3 3 5 n
3 5 8 y
4 8 9 y
5 11 15 n
6 2 5 n
7 1 5 n
7 5 10 y
7 10 14 n
10 rows selected.
答案 1 :(得分:0)
一种方法是将其分为三个重叠条件,主要是“之前”,“期间”和“之后”。因为您希望每个现有行有多个不同的行,所以可以使用union all
执行此操作:
select t.id, t.lowerlimit,
least(tm.lowerlimit, t.upperlimit) as upperlimit,
'n' as overlaps
from t join
t tm
on t.id <> 1 and tm.id = 1 and
t.lowerlimit < tm.lowerlimit
union all
select t.id,
greatest(t.lowerlimit, tm.lowerlimit),
least(t.upperlimit, tm.upperlimit), 'y' as overlaps
from t join
t tm
on t.id <> 1 and tm.id = 1 and
t.lowerlimit <= tm.lowerlimit and
t.upperlimit >= tm.upperlimit
union all
select t.id, greatest(tm.upperlimit, t.upperlimit),
t.upperlimit, 'n'
from t join
t tm
on t.id <> 1 and tm.id = 1 and
t.upperlimit > tm.upperlimit;