1

我有下表:

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 当前在 2019 年 1 月 1 日至 2019 年 1 月 7 日期间预订。如果在 2018 年 12 月 26 日至 2019 年 1 月 3 日期间为房间 5 添加了另一个预订,我希望我的触发器阻止此数据添加到预订中桌子。如果在 2019 年 1 月 3 日至 2019 年 1 月 10 日以及 2019 年 1 月 2 日至 2019 年 1 月 6 日期间进行预订,则同样适用。

基本上预订开始和结束日期不能与同一酒店房间的其他预订开始和结束日期重叠。

这是我到目前为止所尝试的:

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; 

我收到一条错误消息,提示“表级触发器中不允许新或旧引用”。我知道如果我把它变成一个行级触发器,可能会抛出一个变异表错误。

谁能指出错误是什么?

干杯!

4

2 回答 2

1

一个插入语句可以插入多行。例如:

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;

这些插入以任意顺序发生,因此您不能真正接受一行而不接受另一行。相反,您必须拒绝整个插入语句。如果可以进行更新,当然更新也是如此。

因此,您将编写一个 after 语句触发器,您可以在其中查看表中的新情况。

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;

如果表很大,并且您只想查看插入/更新的行以使此触发器快速运行,则必须编写一个复合触发器,而不是在其中记住在行级别的数组中预订 ID 并且只查看这些行在语句级别。

于 2019-10-23T11:20:33.160 回答
0

默认情况下,触发器是为statement levelwhere:new:old不允许创建的,您只需将触发器声明为for each row

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

干杯!!

于 2019-10-23T10:25:15.690 回答