Непосредственная проблема заключается в том, что вы используете WHEN
, который является частью конструкции CASE
. Вы должны были бы использовать IF
здесь:
IF t then
INSERT INTO BOOKING(hotelID,roomNo,guestID,startDate,endDate) ...
END IF;
Но это не так, как триггеры работы - вы не вставить снова внутри них, вы будете пытаться вставить точную копию строки вы вставляете, что приведет к срабатыванию вашего триггера. Вероятно, еще раз - надеюсь, вторая вставка увидит первое в курсоре и остановится. Но оба на самом деле будут делать вставку - когда есть конфликт с датами, вы фактически не предотвращаете вставку, вы просто не пытаетесь дублировать; и второй все равно будет вставлен, так что вы получите две одинаковые строки. Обычный способ предотвращения появления вставки заключается в том, что триггер вызывает исключение; концептуально:
IF NOT t THEN
RAISE_APPLICATION_ERROR(-20001, 'Overlapping dates');
END IF;
Но это все еще не сработает - у вас есть как минимум три других проблемы. Во-первых, вы не можете (легко) запросить таблицу, в которую вы вставляете; вы получите ошибку ORA-04091 «ошибка в работе». Во-вторых, когда вы ссылаетесь на новые значения, вам нужно префикс их с двоеточием, поэтому :newTuple.hotelID
и т. Д. И, в-третьих, проблема параллелизма; две строки, вставляемые одновременно с перекрывающимися датами, не будут видеть друг друга, и оба будут успешными. (И не строго ошибка, но цикл по всем записям, чтобы найти совпадение, будет неэффективным - почему не только искать существующие строки, которые вступают в противоречие с введенными датами?)
Триггер, похоже, не является соответствующий способ принудительного применения этого ограничения.
ОК, это не на самом деле получает ошибку Mutating таблицы:
create table booking(hotelid number, roomno number, guestid number,
startdate date, enddate date);
create or replace trigger mytrigger
before insert on booking
referencing new as new
for each row
declare
cnt number;
begin
select count(*) into cnt from booking
where hotelid = :new.hotelid
and roomno = :new.roomno
and not (enddate < :new.startdate or startdate > :new.enddate);
if cnt > 0 then
raise_application_error(-20001, 'Overlapping dates');
end if;
end;
/
TRIGGER MYTRIGGER compiled
Вставки некоторых данных:
insert into booking values (1, 1, 1, date '2013-02-28', date '2013-03-05');
1 rows inserted.
insert into booking values (1, 1, 2, date '2013-02-27', date '2013-03-01');
Error starting at line 24 in command:
insert into booking values (1, 1, 2, date '2013-02-27', date '2013-03-01')
Error report:
SQL Error: ORA-20001: Overlapping dates
ORA-06512: at "STACKOVERFLOW.MYTRIGGER", line 10
ORA-04088: error during execution of trigger 'STACKOVERFLOW.MYTRIGGER'
insert into booking values (1, 1, 3, date '2013-03-05', date '2013-03-06');
Error starting at line 25 in command:
insert into booking values (1, 1, 3, date '2013-03-05', date '2013-03-06')
Error report:
SQL Error: ORA-20001: Overlapping dates
ORA-06512: at "STACKOVERFLOW.MYTRIGGER", line 10
ORA-04088: error during execution of trigger 'STACKOVERFLOW.MYTRIGGER'
insert into booking values (1, 1, 4, date '2013-03-06', date '2013-03-07');
1 rows inserted.
Две попытки ввести перекрывающуюся дату получила наше -20001 исключения , и были вставлены только два неперекрывающихся ряда:
select * from booking;
HOTELID ROOMNO GUESTID STARTDATE ENDDATE
---------- ---------- ---------- ---------- ----------
1 1 1 28/02/2013 05/03/2013
1 1 4 06/03/2013 07/03/2013
Но у вас все еще есть проблема параллелизма, поскольку два сеанса могут вставлять перекрывающиеся данные одновременно. Поскольку оба они будут неблокированы, select count(*)
в каждом экземпляре триггера не увидит другого, поэтому они оба будут сообщать о нуле, и не будут создавать исключение, и оба будут вставлены.
Есть ли когда конструкция в oracle pl/sql? Если нет, вы можете использовать оператор IF, как вы использовали выше. –