Как удалить (вычесть) из одного диапазона дат другогой диапазон дат?
Есть две таблицы с двумя диапазонами дат, которые пересекаются:
create table t1 (df,dl) as
select date'2020-01-01' df, date'2020-06-24' dl from dual
create table t2 (df,dl) as
select date'2020-05-01' df, date'2020-05-31' dl from dual
Как удалить (вычесть) из диапазона дат одного диапазона дат, диапазон другого, то есть, где пересекается?
Ожидаемый результат:
df | dl
-----------------------
01.01.2020 | 30.04.2020
01.06.2020 | 24.04.2020
Или визуально так:
[-------------------------]
[------]
=
[--------]
[-------]
или так:
[-------------------------]
[-------------------]
=
[--------]
Ответы (1 шт):
Простого и удобного способа вычитать диапазоны дат в Oracle я не нашел, поэтому попробовал написать запрос вручную.
«Остатки» от вычитания получаются в двух случаях:
- Если начало первого отрезка раньше начала второго.
- Если конец первого отрезка позже конца второго.
Построим запросы для каждого случая в отдельности и сольем результаты:
select t1.df, least(t1.dl, t2.df - 1) as dl
from t1
join t2
on t1.df < t2.df
union all
select greatest(t2.dl + 1, t1.df) as df, t1.dl
from t1
join t2
on t1.dl > t2.dl
Стоит иметь ввиду, что если второй отрезок полностью перекрывает первый, то разница совсем не попадет в результат. К тому же запрос предполагает что все отрезки «невырожденные» и df всегда раньше dl.
Проверим на нескольких примерах:
create table t1 (id,df,dl) as
select 1 as id, date'2020-01-01' df, date'2020-06-24' dl from dual
union all
select 2 as id, date'2020-02-01' df, date'2020-05-15' dl from dual
union all
select 3 as id, date'2020-05-10' df, date'2020-07-01' dl from dual
union all
select 4 as id, date'2020-05-01' df, date'2020-05-31' dl from dual;
create table t2 (df,dl) as
select date'2020-05-01' df, date'2020-05-31' dl from dual;
select *
from (select t1.id, t1.df, least(t1.dl, t2.df - 1) as dl
from t1
join t2
on t1.df < t2.df
union all
select t1.id, greatest(t2.dl + 1, t1.df) as df, t1.dl
from t1
join t2
on t1.dl > t2.dl)
order by id, df, dl
Получаем результаты:
| ID | DF | DL |
|---|---|---|
| 1 | 01.01.2020 | 30.04.2020 |
| 1 | 01.06.2020 | 24.06.2020 |
| 2 | 01.02.2020 | 30.04.2020 |
| 3 | 01.06.2020 | 01.07.2020 |