假设在Oracle 10g中,我有一个这样的表:
id | start_date | end_date | value | parent_id
----------------------------------------------------
1 | 01-01-2001 | 01-01-2002 | aaaaaa | 3
2 | 01-01-2003 | 01-01-2004 | bbbbbb | 3
3 | 01-01-2000 | 01-01-2005 | cccccc | 4
4 | 01-01-1999 | 01-01-2006 | dddddd |null
我希望看到父母填补的空白没有重叠。更具体地说,结果应该是这样的:
start_date | end_date | value | depth
-----------------------------------------
01-01-1999 | 01-01-2000 | dddddd | 2
01-01-2000 | 01-01-2001 | cccccc | 1
01-01-2001 | 01-01-2002 | aaaaaa | 0
01-01-2002 | 01-01-2003 | cccccc | 1
01-01-2003 | 01-01-2004 | bbbbbb | 0
01-01-2004 | 01-01-2005 | cccccc | 1
01-01-2005 | 01-01-2006 | dddddd | 2
深度是达到该值的父级数,但可以超过2,因此最好使用递归。
您可以假设具有相同父级的句点没有重叠。所有这些都不使用存储过程,但随意带来CTE,窗口函数等。
答案 0 :(得分:1)
是的,这是可能的。不知道它会有多好。
查询分为3个主要CTE:
row_number()
窗口函数按深度标记更“理想”的行。查询:
with date_ranges_cte as (
select add_months(date '1999-01-01', (rownum-1) * 12) as start_date,
add_months(date '1999-01-01', rownum * 12) as end_date
from dual
connect by rownum <= 7
), max_level_cte as (
select max(level) as max_level
from tbl
start with parent_id is null
connect by prior id = parent_id
), main_cte as (
select d.start_date,
d.end_date,
t.value,
t.lvl,
row_number() over (partition by d.start_date, d.end_date order by t.lvl desc, t.id) as rn
from date_ranges_cte d
join (select t.*, level as lvl
from tbl t
start with parent_id is null
connect by prior id = parent_id) t
on ((d.start_date >= t.start_date and d.start_date < t.end_date)
or (d.end_date > t.start_date and d.end_date <= t.end_date))
)
select m.start_date,
m.end_date,
m.value,
l.max_level - m.lvl as depth
from main_cte m
cross join max_level_cte l
where m.rn = 1
order by m.start_date
如果你可以改变depth
的定义,那么我们可以摆脱其中一个CTE:
with date_ranges_cte as (
select add_months(date '1999-01-01', (rownum-1) * 12) as start_date,
add_months(date '1999-01-01', rownum * 12) as end_date
from dual
connect by rownum <= 7
), main_cte as (
select d.start_date,
d.end_date,
t.value,
t.lvl,
row_number() over (partition by d.start_date, d.end_date order by t.lvl desc, t.id) as rn
from date_ranges_cte d
join (select t.*, level as lvl
from tbl t
start with parent_id is null
connect by prior id = parent_id) t
on ((d.start_date >= t.start_date and d.start_date < t.end_date)
or (d.end_date > t.start_date and d.end_date <= t.end_date))
)
select start_date,
end_date,
value,
lvl - 1 as depth
from main_cte
where rn = 1
order by start_date