这是一种方法,虽然它需要创建一个全局临时表,因为我不知道如何将行合并回 xxa(临时脑放屁,或者真的不可行;我不确定!开放的想法 *{:-) )。无论如何,除了作为下面插入/删除语句的包装器之外,不需要 PL/SQL。
create table xxa (item varchar2(5),
price number,
start_date date,
end_date date);
create table xxb (item varchar2(5),
price number,
start_date date,
end_date date);
create global temporary table xxa_xxb_tmp_res (item varchar2(5),
price number,
start_date date,
end_date date);
insert into xxa
select 'plug', 12, to_date('10/01/2015', 'dd/mm/yyyy'), to_date('15/01/2015', 'dd/mm/yyyy') from dual union all
select 'plug', 1, to_date('20/01/2015', 'dd/mm/yyyy'), to_date('25/01/2015', 'dd/mm/yyyy') from dual union all
select 'plug', 3, to_date('30/01/2015', 'dd/mm/yyyy'), to_date('04/02/2015', 'dd/mm/yyyy') from dual union all
select 'plug', 4, to_date('05/02/2015', 'dd/mm/yyyy'), to_date('10/02/2015', 'dd/mm/yyyy') from dual union all
select 'plug', 43, to_date('20/02/2015', 'dd/mm/yyyy'), to_date('25/02/2015', 'dd/mm/yyyy') from dual;
insert into xxb
select 'plug', 345, to_date('01/01/2015', 'dd/mm/yyyy'), to_date('10/01/2015', 'dd/mm/yyyy') from dual union all
select 'plug', 133, to_date('12/01/2015', 'dd/mm/yyyy'), to_date('20/01/2015', 'dd/mm/yyyy') from dual union all
select 'plug', 344, to_date('27/01/2015', 'dd/mm/yyyy'), to_date('03/02/2015', 'dd/mm/yyyy') from dual union all
select 'plug', 455, to_date('07/02/2015', 'dd/mm/yyyy'), to_date('10/02/2015', 'dd/mm/yyyy') from dual union all
select 'plug', 431, to_date('17/02/2015', 'dd/mm/yyyy'), to_date('23/02/2015', 'dd/mm/yyyy') from dual;
commit;
insert into xxa_xxb_tmp_res (item, price, start_date, end_date)
with date_range as (select least(xa.min_start_date, xb.min_start_date) range_start,
greatest(xa.max_end_date, xb.max_end_date) range_end
from (select min(start_date) min_start_date,
max(end_date) max_end_date
from xxa) xa
cross join
(select min(start_date) min_start_date,
max(end_date) max_end_date
from xxb) xb),
dates as (select range_start + level -1 dt
from date_range
connect by range_start + level -1 <= range_end),
pivot_xxa as (select xxa.item,
xxa.price,
dts.dt
from xxa
inner join dates dts on (dts.dt between xxa.start_date and xxa.end_date)),
pivot_xxb as (select xxb.item,
xxb.price,
dts.dt
from xxb
inner join dates dts on (dts.dt between xxb.start_date and xxb.end_date)),
res as (select coalesce(pxb.item, pxa.item) item,
coalesce(pxb.price, pxa.price) price,
coalesce(pxa.dt, pxb.dt) dt,
row_number() over (partition by coalesce(pxb.item, pxa.item) order by coalesce(pxa.dt, pxb.dt))
- row_number() over (partition by coalesce(pxb.item, pxa.item), coalesce(pxb.price, pxa.price) order by coalesce(pxa.dt, pxb.dt)) grp
from pivot_xxa pxa
full outer join pivot_xxb pxb on (pxa.item = pxb.item
and pxa.dt = pxb.dt))
select item,
price,
min(dt) start_date,
max(dt) end_date
from res
group by item,
price,
grp;
delete from xxa where item in (select item from xxa_xxb_tmp_res);
insert into xxa (item, price, start_date, end_date)
select item, price, start_date, end_date
from xxa_xxb_tmp_res;
commit;
select * from xxa
order by item, start_date;
ITEM PRICE START_DATE END_DATE
----- ---------- ---------- ----------
plug 345 01/01/2015 10/01/2015
plug 12 11/01/2015 11/01/2015
plug 133 12/01/2015 20/01/2015
plug 1 21/01/2015 25/01/2015
plug 344 27/01/2015 03/02/2015
plug 3 04/02/2015 04/02/2015
plug 4 05/02/2015 06/02/2015
plug 455 07/02/2015 10/02/2015
plug 431 17/02/2015 23/02/2015
plug 43 24/02/2015 25/02/2015
drop table xxa;
drop table xxb;
drop table xxa_xxb_tmp_res;
大部分工作是在第一个插入语句中完成的 - 基本上,我正在做的是将每个表中的范围扩展为单独的行,然后将它们完全外部连接,然后优先选择 xxb 表的列xxa的。获得这些信息后,我会使用 tabibitosan 方法将结果重新分组到范围中。
好的,这是一种使用合并的方法,但这确实意味着 xxa 表需要有一个主键(或标识每个项目的行的其他列)。注:如果 xxa 表中的行数多于组合 xxa/xxb 结果的行数,则“额外”行将在 price、start_date 和 end_date 列中包含空值。 (我仍然认为应该删除这些行!):
create table xxa (pk_col number,
item varchar2(5),
price number,
start_date date,
end_date date,
constraint xxa_pk primary key (pk_col));
create table xxb (item varchar2(5),
price number,
start_date date,
end_date date);
create sequence xxa_seq
start with 1
maxvalue 999999999999999999999999999
minvalue 1
nocycle
cache 20
noorder;
insert into xxa
select xxa_seq.nextval,
item,
price,
start_date,
end_date
from (select 'plug' item, 12 price, to_date('10/01/2015', 'dd/mm/yyyy') start_date, to_date('15/01/2015', 'dd/mm/yyyy') end_date from dual union all
select 'plug', 1, to_date('20/01/2015', 'dd/mm/yyyy'), to_date('25/01/2015', 'dd/mm/yyyy') from dual union all
select 'plug', 3, to_date('30/01/2015', 'dd/mm/yyyy'), to_date('04/02/2015', 'dd/mm/yyyy') from dual union all
select 'plug', 4, to_date('05/02/2015', 'dd/mm/yyyy'), to_date('10/02/2015', 'dd/mm/yyyy') from dual union all
select 'fork', 10, to_date('02/01/2015', 'dd/mm/yyyy'), to_date('20/01/2015', 'dd/mm/yyyy') from dual union all
select 'fork', 20, to_date('22/01/2015', 'dd/mm/yyyy'), to_date('28/01/2015', 'dd/mm/yyyy') from dual union all
select 'fork', 30, to_date('01/02/2015', 'dd/mm/yyyy'), to_date('10/02/2015', 'dd/mm/yyyy') from dual union all
select 'club', 10, to_date('02/01/2015', 'dd/mm/yyyy'), to_date('20/01/2015', 'dd/mm/yyyy') from dual union all
select 'club', 20, to_date('22/01/2015', 'dd/mm/yyyy'), to_date('28/01/2015', 'dd/mm/yyyy') from dual union all
select 'club', 30, to_date('01/02/2015', 'dd/mm/yyyy'), to_date('10/02/2015', 'dd/mm/yyyy') from dual);
insert into xxb
select 'plug', 345, to_date('01/01/2015', 'dd/mm/yyyy'), to_date('10/01/2015', 'dd/mm/yyyy') from dual union all
select 'plug', 133, to_date('12/01/2015', 'dd/mm/yyyy'), to_date('20/01/2015', 'dd/mm/yyyy') from dual union all
select 'plug', 344, to_date('27/01/2015', 'dd/mm/yyyy'), to_date('03/02/2015', 'dd/mm/yyyy') from dual union all
select 'plug', 455, to_date('07/02/2015', 'dd/mm/yyyy'), to_date('10/02/2015', 'dd/mm/yyyy') from dual union all
select 'plug', 431, to_date('17/02/2015', 'dd/mm/yyyy'), to_date('23/02/2015', 'dd/mm/yyyy') from dual union all
select 'fork', 15, to_date('15/01/2015', 'dd/mm/yyyy'), to_date('18/01/2015', 'dd/mm/yyyy') from dual union all
select 'fork', 25, to_date('24/01/2015', 'dd/mm/yyyy'), to_date('08/02/2015', 'dd/mm/yyyy') from dual union all
select 'club', 100, to_date('03/01/2015', 'dd/mm/yyyy'), to_date('20/02/2015', 'dd/mm/yyyy') from dual;
commit;
merge into xxa tgt
using (with date_range as (select least(xa.min_start_date, xb.min_start_date) range_start,
greatest(xa.max_end_date, xb.max_end_date) range_end
from (select min(start_date) min_start_date,
max(end_date) max_end_date
from xxa) xa
cross join
(select min(start_date) min_start_date,
max(end_date) max_end_date
from xxb) xb),
dates as (select range_start + level -1 dt
from date_range
connect by range_start + level -1 <= range_end),
pivot_xxa as (select xxa.item,
xxa.price,
dts.dt
from xxa
inner join dates dts on (dts.dt between xxa.start_date and xxa.end_date)),
pivot_xxb as (select xxb.item,
xxb.price,
dts.dt
from xxb
inner join dates dts on (dts.dt between xxb.start_date and xxb.end_date)),
res as (select coalesce(pxb.item, pxa.item) item,
coalesce(pxb.price, pxa.price) price,
coalesce(pxa.dt, pxb.dt) dt,
row_number() over (partition by coalesce(pxb.item, pxa.item) order by coalesce(pxa.dt, pxb.dt))
- row_number() over (partition by coalesce(pxb.item, pxa.item), coalesce(pxb.price, pxa.price) order by coalesce(pxa.dt, pxb.dt)) grp
from pivot_xxa pxa
full outer join pivot_xxb pxb on (pxa.item = pxb.item
and pxa.dt = pxb.dt)),
final_res as (select item,
price,
min(dt) start_date,
max(dt) end_date,
row_number() over (partition by item order by min(dt)) rn
from res
group by item,
price,
grp),
xxa_rn as (select pk_col,
item,
price,
start_date,
end_date,
row_number() over (partition by item order by start_date) rn
from xxa)
select coalesce(fr.item, xa.item) item,
fr.price,
fr.start_date,
fr.end_date,
xa.pk_col
from final_res fr
full outer join xxa_rn xa on (fr.item = xa.item and fr.rn = xa.rn)) src
on (tgt.item = src.item and tgt.pk_col = src.pk_col)
when matched then
update set tgt.price = src.price,
tgt.start_date = src.start_date,
tgt.end_date = src.end_date
when not matched then
insert (pk_col, tgt.item, tgt.price, tgt.start_date, tgt.end_date)
values (xxa_seq.nextval, src.item, src.price, src.start_date, src.end_date);
commit;
select * from xxa
order by item, start_date;
PK_COL ITEM PRICE START_DATE END_DATE
---------- ----- ---------- ---------- ----------
8 club 10 02/01/2015 02/01/2015
9 club 100 03/01/2015 20/02/2015
10 club
5 fork 10 02/01/2015 14/01/2015
6 fork 15 15/01/2015 18/01/2015
7 fork 10 19/01/2015 20/01/2015
27 fork 20 22/01/2015 23/01/2015
26 fork 25 24/01/2015 08/02/2015
22 fork 30 09/02/2015 10/02/2015
1 plug 345 01/01/2015 10/01/2015
2 plug 12 11/01/2015 11/01/2015
3 plug 133 12/01/2015 20/01/2015
4 plug 1 21/01/2015 25/01/2015
25 plug 344 27/01/2015 03/02/2015
23 plug 3 04/02/2015 04/02/2015
28 plug 4 05/02/2015 06/02/2015
21 plug 455 07/02/2015 10/02/2015
24 plug 431 17/02/2015 23/02/2015
drop table xxa;
drop table xxb;
drop sequence xxa_seq;