2014-09-23 6 views
0

Я пытаюсь найти способ сравнить ряды данных, чтобы найти пробелы, однако мне нужно исключить диапазоны дат, которые полностью находятся в другом диапазоне. Некоторые примеры данных:Поиск пробелов в не последовательных диапазонах дат

PERSON_ID START_DATE END_DATE 
0001  01/05/2014 30/11/2014 
0001  01/06/2014 01/08/2014 
0001  01/07/2014 01/11/2014 
0001  01/12/2014 31/03/2015 

Я знаю, что я могу использовать функцию LEAD для сравнения одной линии на другую, чтобы увидеть, где разрыв, если есть один, например:

SELECT END_DATE 
FROM 
    (SELECT t.*, 
     lead(START_DATE,1) OVER (ORDER BY START_DATE) AS next_date 
    FROM table t 
    ) 
WHERE END_DATE+1<>next_date; 

Этот вопрос что это вернет ложный результат. Второй и третий ряды диапазонов дат полностью содержатся в первом и поэтому не должны включаться в расчеты пробелов. Я знаю, что мне нужно внести поправки в аргумент offset в функции LEAD, но я не уверен в эффективном способе делать это для сотен человек. Есть предположения?

ответ

2

Вы могли бы попробовать что-то вроде:

SELECT person_id 
    , start_date + 1 start_date 
    , end_date - 1 end_date 
FROM 
    (SELECT person_id 
     , end_date start_date 
     , lead(start_date) OVER 
      (PARTITION BY person_id 
      ORDER BY start_date) end_date 
    FROM 
    (SELECT person_id 
      , start_date 
      , max(end_date) KEEP 
       (DENSE_RANK LAST 
       ORDER BY end_date 
         , start_date 
       NULLS LAST) end_date 
     FROM 
     (SELECT person_id 
       , CONNECT_BY_ROOT start_date start_date 
       , end_date 
     FROM 
      (SELECT person_id 
       , start_date 
       , end_date 
       , min(start_date) OVER 
        (PARTITION BY person_id) min_start_date 
       , lag(end_date) OVER 
        (PARTITION BY person_id 
         ORDER BY end_date 
          , start_date) lag_end_date 
      FROM mytable) 
     START WITH 
      ( start_date = min_start_date 
      OR start_date > lag_end_date + 1) 
     CONNECT BY 
       person_id = PRIOR person_id 
      AND start_date > PRIOR start_date 
      AND ( start_date <= PRIOR end_date + 1 
       OR PRIOR end_date IS NULL)) 
     GROUP BY person_id 
      , start_date)) 
WHERE end_date IS NOT NULL 

Это слияние перекрывающихся диапазонов, например, 01-Apr-2014 до 31 мая 2014 года и с 01 мая по 2014 год по 30 июня 2014 года будут рассматриваться как один диапазон от 01 апреля 2014 года по 30 июня 2014 года. Он также будет объединять диапазоны примыкания, например. 01-Apr-2014 до 30 апреля 2014 года и с 01 мая по 2014 год по 31 мая 2014 года будут рассматриваться как один диапазон от 01 апреля 2014 года по 31 мая 2014 года. Вам нужно будет изменить этот запрос, если это не так, как вы хотите относиться к этим условиям.

+0

Привет, DrabJay, спасибо за предложение. Я внедрил ваш код как написанный, но он работает неправильно. У меня есть случай с 2 ​​диапазонами, 31/03/14 - 15/08/14 и 15/08/14 - 31/03/15. Я ожидаю 31/03/14 - 31/03/15, но код не возвращает ничего для этого человека. Любая идея, что может быть неправильным? –

+0

@ChizoEjindu У вас есть два диапазона от 31 марта 2014 года по 15 августа 2014 года и с 15 августа по 2014 год до 31 марта 2015 года. Эти перекрывающиеся диапазоны, согласно моему комментарию, будут объединены в один диапазон от 31 марта 2014 года по 31 марта 2015 года. Ваше требование было «найти пробелы». В этом одиночном диапазоне нет пробелов, и, следовательно, записи не возвращаются. – DrabJay

+0

Ах да, я вижу вашу мысль, извиняюсь за мое непонимание! Теперь я понимаю, что это делает, я могу реализовать остальную часть кода вокруг него. Спасибо за вашу помощь! –

0

Так как вы сказали, что вы получаете ложный положительный результат, вы можете превратить его в позитивный, делая что-то вроде этого:

SELECT * FROM table t where END_DATE 
NOT IN (SELECT END_DATE 
FROM 
(SELECT t.*, 
    lead(START_DATE,1) OVER (ORDER BY START_DATE) AS next_date 
FROM table t 
) 
WHERE END_DATE+1<>next_date); 

Я надеюсь, что это дает вам ключ, чтобы получить то, что вы хотите, не меняя свои аргументы офсетных ,

+0

Hi Neels, спасибо за комментарий, но я думаю, что я не объяснил свою проблему должным образом. Глядя на таблицу на линейной основе, функция LEAD могла бы занять промежуток между строками 3 и 4, которые в изоляции являются правильными. Однако для идентификатора человека это неверно, так как строка 1 полностью охватывает даты в строках 2 и 3, и поэтому строки 2 и 3 должны быть исключены из расчетов. Функция LEAD должна рассчитывать только на линии 1 и строке 4 для данного конкретного примера. –

+0

Не могли бы вы отредактировать свой вопрос, чтобы дать нам все более четкое изображение. – Neels

Смежные вопросы