SQL: триггер для предотвращения вставки недопустимых данных в таблицу - PullRequest
1 голос
/ 23 октября 2019

У меня есть следующая таблица:

CREATE TABLE booking(
booking_id NUMBER(8) NOT NULL;
booking_start DATE NOT NULL;
booking_end DATE NOT NULL;
booking_room NUMBER(3) NOT NULL;
guest_no NUMBER(5) NOT NULL;
);

В этой таблице отслеживаются все бронирования номеров в конкретном отеле. Я хочу написать триггер, чтобы предотвратить добавление бронирований, которые совпадают с датами предыдущих бронирований для определенных номеров в отеле.

Например, предположим, что номер 5 в настоящее время забронирован с 1 января 2019 года по 7 января 2019 года. Если добавится еще одно бронирование для номера 5 с 26 декабря 2018 года по 03 января 2019 года, я хочу, чтобы мой триггер предотвратил эти данные. от добавления в стол заказов. То же самое применимо, если бронирование сделано с 3 января 2019 года по 10 января 2019 года, а также с 2 января 2019 года по 06 января 2019 года.

В основном даты начала и окончания бронирования не могут пересекаться с другими датами начала и окончания бронирования длятот же номер в отеле.

Это то, что я пробовал до сих пор:

CREATE OR REPLACE TRIGGER check_booking_valid
BEFORE INSERT ON booking
BEGIN
    SELECT booking_start
    FROM booking
    WHERE booking_room = :new.booking_room;

    SELECT booking_end
    FROM booking
    WHERE booking_room = :new.booking_room;

    IF :new.booking_start > booking_start AND 
    :new.booking_start < booking_end
    THEN raise_application_error(-20000, 'Invalid booking');

    IF :new.booking_end > booking_start AND 
    :new.booking_end < booking_end
    THEN raise_application_error(-20000, 'Invalid booking');

    IF :new.booking_start > booking_start AND 
    :new.booking_start < booking_end AND
    :new.booking_end > booking_start AND 
    :new.booking_end < booking_end
    THEN raise_application_error(-20000, 'Invalid booking');

    END IF;
END; 

Я получаю сообщение об ошибке, в котором говорится: «НОВЫЕ или СТАРЫЕ ссылки не разрешены в триггерах уровня таблицы». Я знаю, что если я сделаю это в триггере уровня строки, может произойти ошибка изменяющейся таблицы.

Может кто-нибудь указать, что это за ошибка?

Приветствия!

Ответы [ 2 ]

0 голосов
/ 23 октября 2019

Оператор вставки может вставлять несколько строк. Например:

insert into booking(booking_start, booking_end, booking_room, guest_no)
select date '2019-11-01', date '2019-11-10', 4, 10 from dual
union all
select date '2019-11-08', date '2019-11-15', 4, 88 from dual;

Эти вставки происходят в произвольном порядке, поэтому вы не можете принять одну строку, а не другую. Вместо этого вы должны отклонить весь оператор вставки. Конечно, то же самое относится и к обновлениям, если таковые могут быть сделаны.

Соответственно, вы бы написали триггер после оператора, чтобы посмотреть на новую ситуацию в таблице.

CREATE OR REPLACE TRIGGER trg_reject_invalid_bookings
AFTER INSERT OR UPDATE ON booking
DECLARE
  v_count INTEGER;
BEGIN
  SELECT count(*)
  INTO v_count
  FROM booking b1
  WHERE EXISTS
  (
    SELECT *
    FROM booking b2
    WHERE b2.booking_id <> b1.booking_id
    AND b2.booking_room = b1.booking_room
    AND b2.booking_start < b1.booking_end
    AND b2.booking_end > b1.booking_start
  )
  AND rownum = 1; -- it suffices to find one overlapping pair

  IF v_count > 0 THEN
    raise_application_error(-20000, 'Invalid booking');
  END IF;
END trg_reject_invalid_bookings;

Если таблица большая и вы хотите просматривать вставленные / обновленные строки только для того, чтобы этот триггер работал быстро, вам нужно будет написать составной триггер вместо того, чтобы помнить идентификаторы резервирования в массиве на уровне строк и смотреть только наэти строки на уровне выписки.

0 голосов
/ 23 октября 2019

По умолчанию триггер создан для statement level, где :new и :old не разрешены. Вам просто нужно объявить свой триггер как for each row

CREATE OR REPLACE TRIGGER check_booking_valid
BEFORE INSERT ON booking
For each row -- this
BEGIN
.....
.....

Cheers !!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...