Использовать разрывы и приближение островков
Тест в реальном времени: http://sqlfiddle.com/#!18/0174b/3
with gap_detector as
(
select
sys_id, item_id,
date_from, date_to,
case when
lag(date_to)
over(partition by sys_id, item_id order by date_from) >= date_from
then
0
else
1
end as gap
from tbl
)
, grouper as
(
select
sys_id, item_id,
date_from, date_to,
sum(gap) over(partition by sys_id, item_id order by date_from) as grp
from gap_detector
)
select
sys_id, item_id,
min(date_from) as date_from,
max(date_to) as date_to
from grouper
group by sys_id, item_id, grp
Выход:
| sys_id | item_id | date_from | date_to |
|--------|---------|------------|------------|
| 1 | 1 | 2019-01-01 | 2019-02-10 |
| 1 | 1 | 2019-02-15 | 2019-03-22 |
| 1 | 2 | 2019-01-01 | 2019-01-10 |
| 1 | 2 | 2019-01-15 | 2019-01-25 |
Как это работает
Сначала нам нужно определить, перекрывается ли date_to из предыдущей строки (используя lag
) с текущей date_from.
Обратите внимание, что у нас есть независимые наборы date_from, то есть предыдущая строка комбо sys_id
+ item_id
(например, 1,1) не перекрывается с другим комбо sys_id
+ item_id
(например, 1,2).Таким образом, первый предыдущий date_to 1,2
- это не March 22, 2019
, а NULL
.Мы можем правильно определить предыдущую строку каждой комбо sys_id
+ item_id
, разделив их, то есть partition by sys_id, item_id
.
С учетом сказанного, вот как мы можем определить, перекрывается ли date_to из предыдущей строки с текущей date_from:
- Если текущая date_from перекрывается с предыдущей date_to, не изолируйте текущую date_fromиз предыдущей строки мы можем сделать это, присвоив текущей строке значение 0.
- В противном случае, если текущий date_from не перекрывается с предыдущим date_to, изолировать (другими словами
gap
) текущую строкуиз предыдущей строки, пометив текущую строку как пробел, мы можем сделать это, присвоив ей значение 1. Позже нам понадобятся 1 и 0.
Живой тест: http://sqlfiddle.com/#!18/0174b/7
with gap_detector as
(
select
sys_id, item_id,
date_from, date_to,
case when
lag(date_to)
over(partition by sys_id, item_id order by date_from) >= date_from
then
0
else
1
end as gap
from tbl
)
select *
from gap_detector
order by sys_id, item_id, date_from
Вывод:
| sys_id | item_id | date_from | date_to | gap |
|--------|---------|------------|------------|-----|
| 1 | 1 | 2019-01-01 | 2019-01-20 | 1 |
| 1 | 1 | 2019-01-15 | 2019-02-10 | 0 |
| 1 | 1 | 2019-02-15 | 2019-02-20 | 1 |
| 1 | 1 | 2019-02-18 | 2019-03-10 | 0 |
| 1 | 1 | 2019-03-10 | 2019-03-22 | 0 |
| 1 | 2 | 2019-01-01 | 2019-01-10 | 1 |
| 1 | 2 | 2019-01-15 | 2019-01-25 | 1 |
Следующим шагом является группировка островков, которые принадлежат друг другу, путем подведения итогов по маркерам зазора (1 и 0).Промежуточный итог выполняется с помощью sum(gap)
над окном комбо sys_id
+ item_id
.
Каждое окно комбо sys_id
+ item_id
может работать независимо, выполняя для них partition
, т. Е. partition by sys_id, item_id
Живой тест: http://sqlfiddle.com/#!18/0174b/12
with gap_detector as
(
select
sys_id, item_id,
date_from, date_to,
case when
lag(date_to)
over(partition by sys_id, item_id order by date_from) >= date_from
then
0
else
1
end as gap
from tbl
)
, grouper as
(
select
sys_id, item_id,
date_from, date_to,
gap,
sum(gap) over(partition by sys_id, item_id order by date_from) as grp
from gap_detector
)
select sys_id, item_id, date_from, date_to, gap, grp
from grouper
Вывод:
| sys_id | item_id | date_from | date_to | gap | grp |
|--------|---------|------------|------------|-----|-----|
| 1 | 1 | 2019-01-01 | 2019-01-20 | 1 | 1 |
| 1 | 1 | 2019-01-15 | 2019-02-10 | 0 | 1 |
| 1 | 1 | 2019-02-15 | 2019-02-20 | 1 | 2 |
| 1 | 1 | 2019-02-18 | 2019-03-10 | 0 | 2 |
| 1 | 1 | 2019-03-10 | 2019-03-22 | 0 | 2 |
| 1 | 2 | 2019-01-01 | 2019-01-10 | 1 | 1 |
| 1 | 2 | 2019-01-15 | 2019-01-25 | 1 | 2 |
Наконец, теперь, когда мы можем определить, какие острова принадлежат друг другу (обозначается grp
), это просто вопрос выполнения group by
на этих grp
маркерах, чтобы определить, когда date_from и date_to начались в каждой группе (grp
) островов.
Живой тест: http://sqlfiddle.com/#!18/0174b/13
select
sys_id, item_id,
min(date_from) as date_from,
max(date_to) as date_to
from grouper
group by sys_id, item_id, grp
Вывод:
| sys_id | item_id | date_from | date_to |
|--------|---------|------------|------------|
| 1 | 1 | 2019-01-01 | 2019-02-10 |
| 1 | 1 | 2019-02-15 | 2019-03-22 |
| 1 | 2 | 2019-01-01 | 2019-01-10 |
| 1 | 2 | 2019-01-15 | 2019-01-25 |