2013-02-26 4 views
0

У меня есть таблица BOOKING(hotelID, roomNo, guestID, startDate, endDate) , и я хочу, чтобы создать триггер, чтобы сделать проверку (перед вставкой) путем сравнения startDate и endDate, чтобы увидеть, если временной интервал уже приняты другими гостями.Ошибка синтаксиса для перед тем триггером

CREATE OR REPLACE TRIGGER MYTRIGGER 
BEFORE insert ON BOOKING 

referencing new as newTuple 
for each row 

declare 
    t boolean; 
    cursor c is 
    select startDate,endDate from ROOM where hotelID = newTuple.hotelID and ROOMNO = newTuple.roomNo; 
BEGIN 
    t := true; 
    open c; 
    loop 
    fetch c into mStartDate, mEndDate; 
    exit when c%NOTFOUND; 

    if (NOT((newTuple.startDate >= mEndDate and newTuple.endDate >= mEndDate) 
     or(newTuple.startDate <= mStartDate and newTuple.endDate <= mStartDate))) then 
     t := false; 
    end if; 

    end loop; 
    close c; 

    WHEN (t=true) then 
    INSERT INTO BOOKING(hotelID,roomNo,guestID,startDate,endDate) 
    values(newTuple.hotelID, newTuple.roomNo, newTuple.guestID, newTuple.startDate, 
     newTyple.endDate); 
END; 

Но это дает мне синтаксические ошибки сообщения, которые я не знаю, как решить (я новичок в Oracle):

Error(26,3): PLS-00103: Encountered the symbol "WHEN" when expecting one of the following: 
(begin case declare end exception exit for goto if loop mod 
null pragma raise return select update while with 
<an identifier> <a double-quoted delimited-identifier> 
<a bind variable> << continue close current delete fetch lock 
insert open rollback savepoint set sql execute commit forall 
merge pipe purge 
The symbol "case" was substituted for "WHEN" to continue. 
Error(30,4): PLS-00103: Encountered the symbol ";" when expecting one of the following: 
case 
+0

Есть ли когда конструкция в oracle pl/sql? Если нет, вы можете использовать оператор IF, как вы использовали выше. –

ответ

1

Непосредственная проблема заключается в том, что вы используете 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(*) в каждом экземпляре триггера не увидит другого, поэтому они оба будут сообщать о нуле, и не будут создавать исключение, и оба будут вставлены.

+0

Хороший подробный ответ. Действительно, ошибок больше, чем неправильная конструкция CASE. – Yaroslav

+0

это один из вопросов моего проекта курса ... Я совершенно не знаком с оракулом. (Prof использовал DB2 в своих слайдах в качестве примеров, но он рекомендует использовать oracle для проектов). По моему мнению, триггер используется как валидация, которая запускается до или после некоторого выполнения. В этом случае, как мне решить эту проблему, чтобы заставить ее работать? –

+0

Кстати, спасибо, что приложили столько усилий на этом посту. Я очень благодарен, но ваш ответ кажется слишком низким для меня, чтобы понять, так как im полностью noob :) –

1

Вы неправильно используя CASE..WHEN структуру на PL/SQL. Проверьте справочную страницу PL/SQL Control Structures на сайте документации Oracle. Вот еще один пример использования CASE..WHEN на SO question и его ответе.

В основном вам нужно будет изменить этот

WHEN (t=true) then 
INSERT INTO BOOKING(hotelID,roomNo,guestID,startDate,endDate) 
VALUES (newTuple.hotelID, newTuple.roomNo, newTuple.guestID, newTuple.startDate, 
     newTyple.endDate); 

К этому:

CASE 
    WHEN (t=true) THEN INSERT INTO BOOKING(hotelID,roomNo,guestID,startDate,endDate) 
        VALUES (newTuple.hotelID, newTuple.roomNo, newTuple.guestID, 
          newTuple.startDate, newTyple.endDate); 
END CASE; 

Или как это было предложено на комментарий к вашему вопросу, если вы проверяете только одно условие, почему не используется и IF структура? Это будет чище и легче понять/поддерживать.

+0

Спасибо за ваш ответ! После того, как я внес изменения, появились новые ошибки, говорящие, что newTuple необходимо объявить. Я просто путаюсь с синтаксисом здесь ... –

+0

@HughH - когда вы ссылаетесь на вставленные значения, вам нужно префикс их с двоеточием; поэтому ': newTuple.hotelID' и т. д. Я добавил это к списку проблем в своем ответе * 8-) –

Смежные вопросы