Олимпиадная задача по Oracle

Вчера упала интересная задача, которая сводится до элементарной, которая аналогичная олимпиадной задаче Oracle. Хочу ее вынести сюда, и показать ее решение на базе алгоритма "схлопывания".

Итак, есть таблица:
EFF_DTTMEXP_DTTM
01.01.201305.01.2013
05.01.201308.01.2013
08.01.201309.01.2013
11.01.201315.01.2013
15.01.201325.01.2013
28.01.201330.01.2013

Нужно склеить подряд идущие даты и вывести их границы.
То есть ожидаемый результат:
EFF_DTTMEXP_DTTM
28.01.201330.01.2013
11.01.201325.01.2013
01.01.201309.01.2013

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

Реализация алгоритма на Oracle:
WITH
--исходные данные
T0 AS
(
SELECT  TO_DATE('01.01.2013', 'DD.MM.YYYY')EFF_DTTM,   TO_DATE('05.01.2013', 'DD.MM.YYYY')EXP_DTTM FROM DUAL UNION ALL
SELECT  TO_DATE('05.01.2013', 'DD.MM.YYYY')EFF_DTTM,   TO_DATE('08.01.2013', 'DD.MM.YYYY')EXP_DTTM FROM DUAL UNION ALL
SELECT  TO_DATE('08.01.2013', 'DD.MM.YYYY')EFF_DTTM,   TO_DATE('09.01.2013', 'DD.MM.YYYY')EXP_DTTM FROM DUAL UNION ALL
SELECT  TO_DATE('11.01.2013', 'DD.MM.YYYY')EFF_DTTM,   TO_DATE('15.01.2013', 'DD.MM.YYYY')EXP_DTTM FROM DUAL UNION ALL
SELECT  TO_DATE('15.01.2013', 'DD.MM.YYYY')EFF_DTTM,   TO_DATE('25.01.2013', 'DD.MM.YYYY')EXP_DTTM FROM DUAL UNION ALL
SELECT  TO_DATE('28.01.2013', 'DD.MM.YYYY')EFF_DTTM,   TO_DATE('30.01.2013', 'DD.MM.YYYY')EXP_DTTM FROM DUAL
),
--флаг разрыва
T1 AS 
(SELECT T0.*,CASE WHEN LAG(EXP_DTTM) OVER (PARTITION BY 1 ORDER BY EFF_DTTM)< EFF_DTTM 
              THEN 1 
              ELSE 0
           END AS V$NUMHIST 
           FROM T0),
--считаем накопительный флаг непрерывности
T2 AS
 (SELECT T1.*,  SUM(V$NUMHIST) OVER (PARTITION BY 1 ORDER BY EFF_DTTM ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS V$NUMHISTSUM FROM T1),
 
T3 AS
--расчетное поле для группировки
(SELECT T2.*, ROW_NUMBER () OVER (PARTITION BY 1 ORDER BY EFF_DTTM) - 
              ROW_NUMBER () OVER (PARTITION BY 1, T2.V$NUMHISTSUM ORDER BY EFF_DTTM) V$GRCOL
 FROM T2)
--результат
SELECT MIN (T3.EFF_DTTM) AS EFF_DTTM,
       MAX (T3.EXP_DTTM) AS EXP_DTTM
FROM T3
GROUP BY V$GRCOL;

Комментарии