Как рассчитать перекрывающийся диапазон дат в SQL - PullRequest
0 голосов
/ 31 октября 2019

Я пытаюсь определить диапазон дат из списка пересекающихся диапазонов дат. Например, мой диапазон дат ниже.

START_DATE  END_DATE
01-SEP-19   04-OCT-19
01-OCT-19   30-OCT-19
05-OCT-19   20-OCT-19
31-OCT-19   15-NOV-19

Отсюда я хотел бы рассчитать перекрывающийся диапазон дат и оттуда отдельные карманы диапазона дат. Мой желаемый результат ниже

START_DATE  END_DATE
01-SEP-19   30-SEP-19
01-OCT-19   04-OCT-19
05-OCT-19   20-OCT-19
21-OCT-19   30-OCT-19
31-OCT-19   15-NOV-19

Любое предложение, как это сделать, будет полезно.

Тестирование в Oracle 13

Ответы [ 4 ]

2 голосов
/ 31 октября 2019

Вы можете использовать следующий запрос:

-- Your Test data:
WITH r(START_DATE,  END_DATE)  AS 
  (SELECT date'2019-09-01', date'2019-10-04' from dual union all
   SELECT date'2019-10-01', date'2019-10-30' from dual union all
   SELECT date'2019-10-05', date'2019-10-20' from dual union all
   SELECT date'2019-10-31', date'2019-11-15' from dual)
   , start_dates AS (SELECT start_date FROM r
                      UNION
                     -- Add the end dates that lie within one of the given ranges:
                     SELECT end_date + 1 FROM r e
                      WHERE EXISTS (SELECT 1 FROM r 
                                     WHERE e.end_date + 1  BETWEEN r.start_date AND r.end_date))
SELECT s.start_date
     , LEAST (-- Get the minimal end_date where the start_date is within the range
              (SELECT MIN(end_date) FROM r
                WHERE s.start_date BETWEEN r.start_date AND r.end_date)
              -- If there is start_date -1 prior to this end_date take this instead
              ,NVL((SELECT MIN(start_date) - 1 FROM r
                     WHERE s.start_date < r.start_date)
                  ,DATE'9999-12-31')) AS END_DATE
  FROM start_dates s

Результат:

START_DATE  END_DATE
01-SEP-19   30-SEP-19
01-OCT-19   04-OCT-19
05-OCT-19   20-OCT-19
21-OCT-19   30-OCT-19
31-OCT-19   15-NOV-19
1 голос
/ 31 октября 2019

Вы включаете дату окончания в диапазон дат. Это работает, только если дата окончания считается самим диапазоном дат, начиная с полуночи до полуночи. Поскольку «даты» Oracle включают компонент времени, я настоятельно рекомендую рассматривать «дату» Oracle как точку во времени. В этом случае гораздо лучше сказать, что конечное время и дата не включены в диапазон. В этом подходе применяется стандарт SQL «Временная достоверность».

В качестве бонуса использование «исключительных» дат окончания облегчает подобные проблемы.

Я работаю над этим, добавляя 1 к дате окончания вначало запроса и вычитание 1 перед завершением запроса. См. https://stewashton.wordpress.com/2018/11/26/ranges-with-nulls-05-segments/ для объяснения и того, что делать с NULL в диапазонах дат.

create table t(START_DATE, END_DATE) as
select to_date('01-SEP-19','dd-mon-yy'), to_date('04-OCT-19','dd-mon-yy') from dual union all
select to_date('01-OCT-19','dd-mon-yy'), to_date('30-OCT-19','dd-mon-yy') from dual union all
select to_date('05-OCT-19','dd-mon-yy'), to_date('20-OCT-19','dd-mon-yy') from dual union all
select to_date('31-OCT-19','dd-mon-yy'), to_date('15-NOV-19','dd-mon-yy') from dual;

with unpivoted as (
  select * from (
    select start_date, end_date + 1 end_date from t
  )
  unpivot(start_date for seg_start in(start_date as 1, end_date as -1))
)
select start_date, end_date - 1 end_date, NUM_SEGS
from (
  select u.*,
  lead(start_date) over(order by start_date) end_date,
  sum(seg_start) over(order by start_date) num_segs
  from unpivoted u
)
where num_segs > 0 and start_date < end_date;

START_DATE END_DATE     NUM_SEGS
---------- ---------- ----------
2019-09-01 2019-09-30          1
2019-10-01 2019-10-04          2
2019-10-05 2019-10-20          2
2019-10-21 2019-10-30          1
2019-10-31 2019-11-15          1

С наилучшими пожеланиями, Stew Ashton

0 голосов
/ 31 октября 2019

Это обеспечит диапазоны дат, которым я верю. И после того, как этот диапазон дат найден, мы можем сделать еще одно левое соединение с главной таблицей, чтобы определить, какая (начальная или конечная дата) подпадает под эти диапазоны дат.

create table date_range
as 
select * 
from  
( 
    select "START_DATE" as dt
    from "CUST_name" 
    union
    select "END_DATE" 
    from "CUST_name" 
    union 
    select "START_DATE"
    from "CUST_PH" 
    union
    select "END_DATE" 
    from "CUST_PH" 
) x 
order by dt

select dt, nvl((LEAD (dt,1) OVER (ORDER BY dt)) -1, date '9999-12-31') AS next_dt
from date_range order by dt

DT  NEXT_DT
01-SEP-19   30-SEP-19
01-OCT-19   03-OCT-19
04-OCT-19   04-OCT-19
05-OCT-19   19-OCT-19
20-OCT-19   29-OCT-19
30-OCT-19   30-OCT-19
31-OCT-19   14-NOV-19
15-NOV-19   31-DEC-99
0 голосов
/ 31 октября 2019

Вы можете попробовать следующий запрос для перекрывающегося диапазона дат в SQL:

 select * from t t1
          join t t2 on 
             (t1.START_DATE > t2.START_DATE and t1.START_DATE < t2.END_DATE  )
          or (t1.END_DATE   > t2.START_DATE and t1.END_DATE   < t2.END_DATE  )
          or (t1.END_DATE   > t2.END_DATE   and t1.START_DATE < t2.START_DATE)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...