16 сент. 2013 г.

Пересечение периодов в Oracle

Первая ситуация - проверка перекрытия и вывод ошибки

Исходные данные: таблица some_table с ключевым полем key_field и датой действия периода (поля date_from и date_till)
Требования: при вставке записи в таблицу проверять пересечение нового периода с уже существующими в таблице, и в случае перекрытия выводить ошибку.
Реализация: создается триггер на таблицу

CREATE OR REPLACE TRIGGER some_table_aiu_trg
 AFTER
  INSERT OR UPDATE
 ON some_table
REFERENCING NEW AS NEW OLD AS OLD
DECLARE
   l_dummy varchar2(1);
   CURSOR  c_overlap
   IS
     SELECT 'x'
       FROM some_table t1, some_table t2
      WHERE t1.key_field = t2.key_field
        AND t1.date_from <= t2.date_till
        AND t1.date_till >= t2.date_from
        AND t1.date_till <> t2.date_till;
BEGIN

   -- Проверяем, нет ли перекрытия
   OPEN c_overlap;
   FETCH c_overlap INTO l_dummy;
      IF c_overlap%found THEN
         CLOSE c_overlap;
         raise_application_error(-20050,'Period overlap another period');
      END IF;
   CLOSE c_overlap;

END;
/

Вторая ситуация - сдвиг времени действия старых периодов и вставка нового периода

Исходные данные: таблица some_table с ключевым полем key_field и датой действия периода (поля date_from и date_till)
Требования: при вставке записи в таблицу проверять пересечение нового периода с уже существующими в таблице.
Если пересечения нет - вставляем запись.
Если пересечение есть - сдвигаем даты "от" и "до" старых периодов на 1 секунду, и вставляем запись. Если новый период полностью перекрывает старых - удаляем старую запись.
Реализация:

DECLARE
    l_date_from  date      := to_date('10.07.2013','dd.mm.yyyy');
    l_date_till  date      := to_date('22.07.2013','dd.mm.yyyy');
    l_key_field  number(5) := 1;
BEGIN

    -- сдвиг предыдущих периодов
    DELETE FROM some_table t
    WHERE t.key_field = l_key_field
      AND t.date_from >= l_date_from
      AND t.date_till <= l_date_till;

    UPDATE some_table t
    SET t.date_till = l_date_from - 1/(24*60*60)
    WHERE t.key_field = l_key_field
      AND t.date_from < l_date_from
      AND t.date_till >= l_date_from;

    UPDATE some_table t
    SET t.date_from = l_date_till + 1/(24*60*60)
    WHERE t.key_field = l_key_field
      AND t.date_from > l_date_from
      AND t.date_from < l_date_till
      AND t.date_till >= l_date_till;

    -- вставка новой записи
    INSERT INTO some_table(key_field, date_from, date_till)
    VALUES (l_key_field, l_date_from, l_date_till);

END;

Комментариев нет: