如何使用外键引用创建检查约束

How to create check constraint with foreign key reference

我正在用 H2 中的这两个 table 创建一个数据库:

CREATE TABLE INSTANCE
( 
     INSTANCE_ID BIGINT AUTO_INCREMENT PRIMARY KEY, 
     DATE DATE, 
     TIME TIME, 
     LOCATION VARCHAR(255), 
     PRICE INT, 
     CAPACITY INT, 
     EVENT_ID INT
);

CREATE TABLE RESERVATION
( 
     RESERVATION_ID BIGINT AUTO_INCREMENT PRIMARY KEY, 
     RESERVATION_ORDER INT NOT NULL, 
     INSTANCE_ID INT NOT NULL, 
     USER_ID INT NOT NULL
);

例如,有一个容量为 20 的实例。

预订顺序随着对此实例的每次预订而递增。

所以例如像这样:

INSERT INTO RESERVATION VALUES (1,1,1,1);
INSERT INTO RESERVATION VALUES (2,2,1,3);
INSERT INTO RESERVATION VALUES (3,3,1,4);
INSERT INTO RESERVATION VALUES (4,4,1,5);
INSERT INTO RESERVATION VALUES (5,1,3,2);
INSERT INTO RESERVATION VALUES (6,2,3,5);
INSERT INTO RESERVATION VALUES (7,1,6,6);

我的问题是,我需要创建

INSTANCE.CAPACITY >= RESERVATION_ORDER 

所以我无法再添加其他预订,因为该实例已经有 20 个预订,但我不知道该怎么做。我一生中只使用过几次 SQL,而且我很难用外键引用其他 table。

我在创建 table 时尝试添加类似的内容,但没有成功

CHECK (RESERVATION_ORDER =< (SELECT CAPACITY FROM INSTANCE WHERE (INSTANCE_ID = INSTANCE_ID)))

在SQL中,检查约束只能引用同一行中的列或用户定义的函数。

您可以通过定义用户定义的函数来做您想做的事。或者您可以定义一个触发器来强制执行约束。

但是,您不能使用简单的检查约束来这样做。

解决此问题的最常用方法如下:

  1. reservations 添加一列,这是当前活动实例数。
  2. 适当地将insert/update/delete触发器添加到instances和increment/decrement上述列。
  3. reservervations 中添加一个检查约束,将计算值与计算计数进行比较。

此检查约束将失败。

与在检查约束中使用 UDF 相比,我更喜欢这种方法,因为计数很容易查看和验证。

您可以定义 UDF 并在 RESERVATION table 上调用该 UDF。如下所示:

CREATE FUNCTION dbo.CheckInstanceCapacity (@event_id int, @capacity int)
RETURNS int
AS 
BEGIN
  DECLARE @value int
    SELECT @value = CASE WHEN CAPACITY >= @capacity THEN 0 ELSE 1 END
    FROM INSTANCE
    WHERE EVENT_ID = @event_id 
  RETURN @value
END;

ALTER TABLE RESERVATION ADD CONSTRAINT InsCapacity 
CHECK (dbo.CheckInstanceCapacity(EVENT_ID, RESERVATION_ORDER) = 0); 

您需要添加 table 名称或别名以区分 INSTANCE_ID 列与 table RESERVATION 来自同一列 table [=14] =] 在你的检查约束中。您还需要一个外键约束。

ALTER TABLE RESERVATION ADD
    FOREIGN KEY (INSTANCE_ID) REFERENCES INSTANCE(INSTANCE_ID);

ALTER TABLE RESERVATION ADD
    CHECK(RESERVATION_ORDER <=
        (SELECT CAPACITY FROM INSTANCE I
            WHERE (RESERVATION.INSTANCE_ID = I.INSTANCE_ID)));

我认为您还应该检查 RESERVATION_ORDER 是否大于或等于 1,方法是将 RESERVATION_ORDER <= (SELECT … 替换为 RESERVATION_ORDER BETWEEN 1 AND (SELECT …

您很可能还需要一个 UNIQUE 约束来防止 RESERVATION 中具有相同 INSTANCE_IDRESERVATION_ORDER 值的重复行。

ALTER TABLE RESERVATION ADD UNIQUE(INSTANCE_ID, RESERVATION_ORDER);

请注意 FOREIGN KEYUNIQUE 约束始终得到保证,但 CHECK 约束 引用其他 tables 中的 H2 仅在您插入或修改具有约束的行时才有效。当从其子查询引用的 table 被修改时,H2 中的 CHECK 约束不会重新验证。因此,您不应该减少 INSTANCE table 中现有行的 CAPACITY,或者在执行此操作时执行一些额外的验证,例如,在触发器中。