我有一张桌子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 :(得分:1)
您在PL / SQL上错误地使用CASE..WHEN
结构。查看Oracle文档站点上的PL/SQL Control Structures手册页。以下是SO question上使用CASE..WHEN的另一个示例及其答案。
基本上你需要改变这个
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
结构?理解/维护起来会更清晰,更容易。
答案 1 :(得分: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
等等。第三,你有一个并发问题;同时插入重叠日期的两行将不会彼此看到,并且两者都将成功。 (并非严格意义上的错误,但是循环遍历所有记录以找到匹配将是低效的 - 为什么不只查找与插入日期冲突的现有行?)
触发器似乎不是强制执行此约束的适当方式。
好的,这实际上并没有出现变异表错误:
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(*)
将看不到另一个,因此它们都将报告零,既不会引发异常,也会插入两者。