如何使用外键引用创建检查约束
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中,检查约束只能引用同一行中的列或用户定义的函数。
您可以通过定义用户定义的函数来做您想做的事。或者您可以定义一个触发器来强制执行约束。
但是,您不能使用简单的检查约束来这样做。
解决此问题的最常用方法如下:
- 向
reservations
添加一列,这是当前活动实例数。
- 适当地将insert/update/delete触发器添加到
instances
和increment/decrement上述列。
- 在
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_ID
和 RESERVATION_ORDER
值的重复行。
ALTER TABLE RESERVATION ADD UNIQUE(INSTANCE_ID, RESERVATION_ORDER);
请注意 FOREIGN KEY
和 UNIQUE
约束始终得到保证,但 CHECK
约束 引用其他 tables 中的 H2 仅在您插入或修改具有约束的行时才有效。当从其子查询引用的 table 被修改时,H2 中的 CHECK
约束不会重新验证。因此,您不应该减少 INSTANCE
table 中现有行的 CAPACITY
,或者在执行此操作时执行一些额外的验证,例如,在触发器中。
我正在用 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中,检查约束只能引用同一行中的列或用户定义的函数。
您可以通过定义用户定义的函数来做您想做的事。或者您可以定义一个触发器来强制执行约束。
但是,您不能使用简单的检查约束来这样做。
解决此问题的最常用方法如下:
- 向
reservations
添加一列,这是当前活动实例数。 - 适当地将insert/update/delete触发器添加到
instances
和increment/decrement上述列。 - 在
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_ID
和 RESERVATION_ORDER
值的重复行。
ALTER TABLE RESERVATION ADD UNIQUE(INSTANCE_ID, RESERVATION_ORDER);
请注意 FOREIGN KEY
和 UNIQUE
约束始终得到保证,但 CHECK
约束 引用其他 tables 中的 H2 仅在您插入或修改具有约束的行时才有效。当从其子查询引用的 table 被修改时,H2 中的 CHECK
约束不会重新验证。因此,您不应该减少 INSTANCE
table 中现有行的 CAPACITY
,或者在执行此操作时执行一些额外的验证,例如,在触发器中。