Сгруппируйте строки по идентификатору и найдите max / min (date_from, date_to) с пробелами в дате - PullRequest
1 голос
/ 29 апреля 2019

Мне нужно сгруппировать данные по идентификатору и найти макс / мин (date_from, date_to).Но если есть пробел в дате, это должна быть новая строка.

У меня есть следующие данные:

SYS_ID  ITEM_ID DATE_FROM   DATE_TO
1       1       01.01.2019  20.01.2019
1       1       15.01.2019  10.02.2019
1       1       15.02.2019  20.02.2019
1       1       18.02.2019  10.03.2019
1       1       10.03.2019  22.03.2019
1       2       01.01.2019  10.01.2019
1       2       15.01.2019  25.01.2019

Результат должен быть:

SYS_ID  ITEM_ID DATE_FROM   DATE_TO
1       1       01.01.2019  10.02.2019
1       1       15.02.2019  22.03.2019
1       2       01.01.2019  10.01.2019
1       2       15.01.2019  25.01.2019

Есть ли способсделать это без использования курсора?

1 Ответ

3 голосов
/ 29 апреля 2019

Использовать разрывы и приближение островков

Тест в реальном времени: 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 |
...