替代在CHECK约束中使用子查询?

时间:2017-02-11 01:29:11

标签: sqlite triggers subquery relational-database check-constraints

我正在尝试建立一个简单的酒店房间登记数据库作为学习练习。

CREATE TABLE HotelReservations
(
    roomNum INTEGER NOT NULL,
    arrival DATE NOT NULL,
    departure DATE NOT NULL,
    guestName CHAR(30) NOT NULL,

    CONSTRAINT timeTraveler CHECK (arrival < departure) /* stops time travelers*/
    /* CONSTRAINT multipleReservations CHECK (my question is about this) */

    PRIMARY KEY (roomNum, arrival)
);

我无法指定一个不允许为尚未腾出的房间插入新预订的约束。例如(下图),客人'B'在'A'结账之前检查房间123。

INSERT INTO HotelStays(roomNum, arrival, departure, guestName)
VALUES 
    (123, date("2017-02-02"), date("2017-02-06"), 'A'),
    (123, date("2017-02-04"), date("2017-02-08"), 'B');

这不应该被允许,但我不确定如何写这个约束。我的第一次尝试是编写一个子查询,但是我无法弄清楚正确的子查询,因为我不知道如何访问新插入的'roomNum'值以执行子查询。然后我还发现大多数SQL系统甚至不允许在check中进行子查询。

那我该怎么写这个约束呢?我读了一些看似可以解决这个问题的触发器,但这真的是唯一的方法吗?或者我只是密集而缺少一种明显的方法来编写约束?

1 个答案:

答案 0 :(得分:4)

documentation确实说:

  

CHECK约束的表达式可能不包含子查询。

虽然可以创建一个返回数据库并查询表的用户定义函数,但实现此约束的唯一合理方法是使用触发器。

有一个特殊的mechanism to access the new row inside the trigger

  

WHEN子句和触发器操作都可以使用表单&#34; NEW。 column-name &#34;的引用来访问要插入,删除或更新的行的元素。和&#34; OLD。 column-name &#34;,其中 column-name 是与触发器关联的表中列的名称。< / p>

CREATE TRIGGER multiple_reservations_check
BEFORE INSERT ON HotelReservations
BEGIN
    SELECT RAISE(FAIL, "reservations overlap")
    FROM HotelReservations
    WHERE roomNum = NEW.roomNum
      AND departure > NEW.arrival
      AND arrival < NEW.departure;
END;