根据时间将行拆分为多行
Split row into multiple rows based on time
假设在 Oracle 10g 中,我有这样一个 table:
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、window 函数等。
是的,这是可能的。虽然不知道它的表现如何。
查询分为 3 个主要 CTE:
- 为输出目的生成所需日期范围的 CTE
- 计算树中最大深度的 CTE
- 一个 CTE,用于将所有数据连接在一起并使用
row_number()
window 函数按深度标记更多 "desirable" 行。
查询:
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
假设在 Oracle 10g 中,我有这样一个 table:
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、window 函数等。
是的,这是可能的。虽然不知道它的表现如何。
查询分为 3 个主要 CTE:
- 为输出目的生成所需日期范围的 CTE
- 计算树中最大深度的 CTE
- 一个 CTE,用于将所有数据连接在一起并使用
row_number()
window 函数按深度标记更多 "desirable" 行。
查询:
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