如何在不同table中根据categoryid添加外键?
How to add Foreign Key based on categoryid in different table?
假设:用户可以通过界面插入输入,输入是关于用于喂养特定猫或狗的食物。
在用户输入 table 中,我们要保存我们正在谈论的猫或狗的食物 ID 和 ID。
我们有这些值,但是:
- 如果 subjectid 是一个 catid,我们如何检查它是否存在于 de cats table。
- 如果 subjectid 是 dogid,我们如何检查它是否存在于 de dogs table。
我想到了以下解决方案,但我得到了'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
)
我不相信你可以有条件外键。根据您的要求,可能还有其他方法可以做到这一点。
- 向 UserInput table 添加两个可为空的列,一个用于 CatId,另一个用于 DogId。添加一个约束以确保一个或另一个为空,但不能同时为空。
- 将猫和狗都存储在 Animal table 中,并用一列来指示动物类型。
- 将 UserInput 拆分为 CatUserInput 和 DogUserInput
您确实需要检查您的用例以确定最佳方法。例如,如果您确实必须将猫和狗分成两个 table,那么您正在为 cat/dog 和 Food.
之间的多对多关系建模
你不能(轻易地)用检查约束做你想做的事。你在这里有一个相当复杂的依赖关系。
两种可能的解决方案是:
- 创建一个执行检查的用户定义函数。虽然不能将复杂的逻辑放在
check
约束中,但可以调用用户定义的函数。
- 使用触发器。
或者,您可以重构数据模型以在 userinput
和 foods
中包含 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 现在可以用作外键。
假设:用户可以通过界面插入输入,输入是关于用于喂养特定猫或狗的食物。
在用户输入 table 中,我们要保存我们正在谈论的猫或狗的食物 ID 和 ID。
我们有这些值,但是:
- 如果 subjectid 是一个 catid,我们如何检查它是否存在于 de cats table。
- 如果 subjectid 是 dogid,我们如何检查它是否存在于 de dogs table。
我想到了以下解决方案,但我得到了'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
)
我不相信你可以有条件外键。根据您的要求,可能还有其他方法可以做到这一点。
- 向 UserInput table 添加两个可为空的列,一个用于 CatId,另一个用于 DogId。添加一个约束以确保一个或另一个为空,但不能同时为空。
- 将猫和狗都存储在 Animal table 中,并用一列来指示动物类型。
- 将 UserInput 拆分为 CatUserInput 和 DogUserInput
您确实需要检查您的用例以确定最佳方法。例如,如果您确实必须将猫和狗分成两个 table,那么您正在为 cat/dog 和 Food.
之间的多对多关系建模你不能(轻易地)用检查约束做你想做的事。你在这里有一个相当复杂的依赖关系。
两种可能的解决方案是:
- 创建一个执行检查的用户定义函数。虽然不能将复杂的逻辑放在
check
约束中,但可以调用用户定义的函数。 - 使用触发器。
或者,您可以重构数据模型以在 userinput
和 foods
中包含 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 现在可以用作外键。