簡體   English   中英

before觸發器的語法錯誤

[英]Syntax error for a before trigger

我有一個表BOOKING(hotelID, roomNo, guestID, startDate, endDate) ,我想創建一個觸發器來進行驗證(插入前),比較startDateendDate ,看看其他客人是否已經采用了時間段。

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   

您在PL / SQL上錯誤地使用CASE..WHEN結構。 查看Oracle文檔站點上的PL / SQL控制結構手冊頁。 這是CASE..WHEN在SO問題及其答案上使用的另一個示例。

基本上你需要改變它

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結構? 理解/維護更清晰,更容易。

當前的問題是你正在使用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等。第三,你有一個並發問題; 同時插入重疊日期的兩行將不會彼此看到,並且兩者都將成功。 (並非嚴格意義上的錯誤,但循環遍歷所有記錄以找到匹配將是低效的 - 為什么不只查找與插入日期沖突的現有行?)

觸發器似乎不是強制執行此約束的適當方法。


好的,這實際上並沒有出現變異表錯誤:

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(*)將看不到另一個,因此它們都將報告零,既不會引發異常,也會插入兩者。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM