假设我有这些表:
CREATE TABLE "account" (
"id" INTEGER PRIMARY KEY
-- more fields
);
CREATE TABLE "product" (
"id" INTEGER PRIMARY KEY,
"accountId" INTEGER NOT NULL,
-- more fields
CONSTRAINT "fk_product_accountId" FOREIGN KEY("accountId") REFERENCES "account"("id") ON UPDATE CASCADE ON DELETE CASCADE
);
CREATE TABLE "tag" (
"id" INTEGER PRIMARY KEY,
"accountId" INTEGER NOT NULL,
-- more fields
CONSTRAINT "fk_tag_accountId" FOREIGN KEY("accountId") REFERENCES "account"("id") ON UPDATE CASCADE ON DELETE CASCADE
);
CREATE TABLE "product_tag" (
"productId" INTEGER NOT NULL,
"tagId" INTEGER NOT NULL,
PRIMARY KEY("productId", "tagId"),
CONSTRAINT "fk_tag_productId" FOREIGN KEY("productId") REFERENCES "product"("id") ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT "fk_product_tagId" FOREIGN KEY("tagId") REFERENCES "tag"("id") ON UPDATE CASCADE ON DELETE CASCADE
);
...换句话说,帐户可以包含与该帐户关联的产品和标签,然后可以将这些标签与这些产品相关联。
有没有办法在数据库级别定义一个约束,用于检查product_tag
组合是否有效,即tag
和product
是否都属于同一个帐户,以便我可以避免在INSERT
声明中检查此有效性?
我在accountId
中想到了一个额外的列product_tag
,但我认为我不能在此定义多个外键,我认为这实际上也不会像我想要的那样检查约束
CHECK
约束是否会提供这种复杂程度?
答案 0 :(得分:1)
这个怎么样?
Account:
AccountID int not null
Tag:
AccountID int not null
TagID int not null
primary key (AccountID, TagID)
foreign key (AccountID) references Account(AccountID)
Product:
AccountID int not null
ProductID int not null
primary key (AccountID, ProductID)
foreign key (AccountID) references Account(AccountID)
ProductTag:
AccountID int not null,
TagID int not null,
ProductID int not null,
primary key(AccountID, TagID, ProductID)
foreign key(AccountID, TagID) references Tag(AccountID, TagID)
foreign key(AccountID, ProductID) references Product(AccountID, ProductID)
使用复合键而不是Tag
和Product
上的身份键,我们可以执行此操作。我发现使用身份密钥(代理密钥)可以具有限制约束的有用性的效果,因为'信息'表格中包含的内容更为分散,而且约束只能在一个或两个表格上工作,松散地说。我非常喜欢这种情况作为这种效果的一个例子。使用第一种设计会迫使我们使用触发器来强制执行规则。
我有兴趣看到其他人'解决方案...
答案 1 :(得分:1)
外键约束仅关注作为约束一部分的列,因此检查帐户ID的唯一方法是使其成为所有键的一部分。
CHECK约束不能包含子查询,因此不能用于此。
使用现有数据库结构处理此问题的唯一方法是触发器:
CREATE TRIGGER check_product_tag_same_account
BEFORE INSERT ON product_tag
FOR EACH ROW
WHEN (SELECT accountId FROM product WHERE id = NEW.productId) !=
(SELECT accountId FROM tag WHERE id = NEW.tagId )
BEGIN
SELECT raise(FAIL, "account IDs do not match");
END;