如何在不同table中根据categoryid添加外键?

How to add Foreign Key based on categoryid in different table?

假设:用户可以通过界面插入输入,输入是关于用于喂养特定猫或狗的食物。

在用户输入 table 中,我们要保存我们正在谈论的猫或狗的食物 ID 和 ID。

我们有这些值,但是:

我想到了以下解决方案,但我得到了'Subqueries are not allowed in this context. Only scalar expressions are allowed.'

CONSTRAINT checksubject CHECK (
    EXISTS (SELECT 1 FROM cats c WHERE c.catid = subjectid) AND (SELECT categoryid FROM foods f WHERE f.foodid = userinput.foodid) = 0
    OR
    EXISTS (SELECT 1 FROM dogs d WHERE d.dogid = subjectid) AND (SELECT categoryid FROM foods f WHERE f.foodid = userinput.foodid) = 1
)

我不相信你可以有条件外键。根据您的要求,可能还有其他方法可以做到这一点。

  1. 向 UserInput table 添加两个可为空的列,一个用于 CatId,另一个用于 DogId。添加一个约束以确保一个或另一个为空,但不能同时为空。
  2. 将猫和狗都存储在 Animal table 中,并用一列来指示动物类型。
  3. 将 UserInput 拆分为 CatUserInput 和 DogUserInput

您确实需要检查您的用例以确定最佳方法。例如,如果您确实必须将猫和狗分成两个 table,那么您正在为 cat/dog 和 Food.

之间的多对多关系建模

你不能(轻易地)用检查约束做你想做的事。你在这里有一个相当复杂的依赖关系。

两种可能的解决方案是:

  1. 创建一个执行检查的用户定义函数。虽然不能将复杂的逻辑放在check约束中,但可以调用用户定义的函数。
  2. 使用触发器。

或者,您可以重构数据模型以在 userinputfoods 中包含 categoryid。可以使用外键约束和计算列来表达这种子集关系。

对于此解决方案,您将从食物的冗余唯一 index/unique 约束开始:

create unique index unq_foods_foodid_categoryid on foods(categoryid, foodid);

然后将categoryid添加到userinput:

alter table userinput add column categoryid int;
alter table userinput add constraint fk_userinput_categoryid_foodid
    foreign key (categoryid, foodid) references foods(categoryid, foodid);

外键引用确保值相同。

然后添加持久计算列:

alter table userinput
    add column catid (case when categoryid = 0 then subjectid end) persisted;

alter table userinput
    add column dogid (case when categoryid = 1 then subjectid end) persisted;

最后添加外键约束:

alter table userinput add contraint fk_userinput_catid
    foreign key (catid) references cats(catid);

alter table userinput add contraint fk_userinput_dogid
    foreign key (dogid) references dogs(dogid);

请注意,持久化列确实占用了 space。但这允许您在不使用自定义代码(即触发器或 UDF)的情况下拥有 "conditional" 外键约束。

狗和猫是泛化宠物的特化。换句话说,狗和猫是宠物的子类。您可以为宠物设置一个 table,并用一个列表示它是猫还是狗。 此 table 的键 PetId 现在可以用作外键。