数据库级约束以确保产品和标签属于同一帐户?

时间:2017-03-27 18:47:51

标签: sqlite foreign-keys constraints

假设我有这些表:

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组合是否有效,即tagproduct是否都属于同一个帐户,以便我可以避免在INSERT声明中检查此有效性?

我在accountId中想到了一个额外的列product_tag,但我认为我不能在此定义多个外键,我认为这实际上也不会像我想要的那样检查约束

CHECK约束是否会提供这种复杂程度?

2 个答案:

答案 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)

使用复合键而不是TagProduct上的身份键,我们可以执行此操作。我发现使用身份密钥(代理密钥)可以具有限制约束的有用性的效果,因为'信息'表格中包含的内容更为分散,而且约束只能在一个或两个表格上工作,松散地说。我非常喜欢这种情况作为这种效果的一个例子。使用第一种设计会迫使我们使用触发器来强制执行规则。

我有兴趣看到其他人'解决方案...

答案 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;