Олимпиадная задача по Oracle
Вчера упала интересная задача, которая сводится до элементарной, которая аналогичная олимпиадной задаче Oracle. Хочу ее вынести сюда, и показать ее решение на базе алгоритма "схлопывания".
Итак, есть таблица:
Нужно склеить подряд идущие даты и вывести их границы.
То есть ожидаемый результат:
Алгоритм более сложный чем необходимо для такого упрощенного случая, но при реальном использовании появятся еще и бизнес поля, тогда алгоритм подойдет очень хорошо. Так как для схлопывания с учетом дырок в истории понадобится еще учет изменения бизнес полей.
Реализация алгоритма на Oracle:
Итак, есть таблица:
EFF_DTTM | EXP_DTTM |
---|---|
01.01.2013 | 05.01.2013 |
05.01.2013 | 08.01.2013 |
08.01.2013 | 09.01.2013 |
11.01.2013 | 15.01.2013 |
15.01.2013 | 25.01.2013 |
28.01.2013 | 30.01.2013 |
Нужно склеить подряд идущие даты и вывести их границы.
То есть ожидаемый результат:
EFF_DTTM | EXP_DTTM |
---|---|
28.01.2013 | 30.01.2013 |
11.01.2013 | 25.01.2013 |
01.01.2013 | 09.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;
Комментарии
Отправить комментарий