Олимпиадная задача по 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;
Комментарии
Отправить комментарий