让我们举一个愚蠢的例子:我有很多家养动物,每个动物都有一个NAME作为id和一个类型(CAT或DOG),让我们这样写(伪代码):
TABLE ANIMALS (
NAME char,
ANIMAL_TYPE char {'DOG', 'CAT'}
PRIMARY KEY(NAME)
)
(例如,我有一个叫做Felix的猫,一只叫做Pluto的狗)
在另一张桌子上,我想为每只动物储存喜欢的食物:
TABLE PREFERED_FOOD (
ANIMAL_NAME char,
PREF_FOOD char
FOREIGN KEY (ANIMAL_NAME) REFERENCES ANIMALS(NAME)
)
(例如,菲利克斯喜欢牛奶,冥王星喜欢骨头)
由于我想定义一组可能的首选食物,我会在第三张表中列出每种动物的食物类型:
TABLE FOOD (
ANIMAL_TYPE char {'DOG', 'CAT'},
FOOD_TYPE char
)
(例如,DOG吃骨头和肉,CAT吃鱼和牛奶)
我的问题出现了:我想在PREFERED_FOOD中添加一个外部约束,因为PREF_FOOD是来自FOOD的FOOD_TYPE,其中FOOD.ANIMAL_TYPE = ANIMALS.TYPE。如何在不复制PREFERED_FOOD上的ANIMAL_TYPE的情况下定义此外键?
我不是SQL的专家,所以如果真的很容易你就可以叫我傻了! - )
答案 0 :(得分:3)
你不能在SQL中。如果SQL支持断言,我认为可以。 (SQL-92标准定义了断言。据我所知,还没有人支持它们。)
要解决该问题,请使用重叠约束。
-- Nothing special here.
create table animal_types (
animal_type varchar(15) primary key
);
create table animals (
name varchar(15) primary key,
animal_type varchar(15) not null references animal_types (animal_type),
-- This constraint lets us work around SQL's lack of assertions in this case.
unique (name, animal_type)
);
-- Nothing special here.
create table animal_food_types (
animal_type varchar(15) not null references animal_types (animal_type),
food_type varchar(15) not null,
primary key (animal_type, food_type)
);
-- Overlapping foreign key constraints.
create table animals_preferred_food (
animal_name varchar(15) not null,
-- This column is necessary to implement your requirement.
animal_type varchar(15) not null,
pref_food varchar(10) not null,
primary key (animal_name, pref_food),
-- This foreign key constraint requires a unique constraint on these
-- two columns in "animals".
foreign key (animal_name, animal_type)
references animals (animal_name, animal_type),
-- Since the animal_type column is now in this table, this constraint
-- is simple.
foreign key (animal_type, pref_food)
references animal_food_types (animal_type, food_type)
);
答案 1 :(得分:0)
FOREIGN KEY (PREF_FOOD) REFERENCES FOOD (FOOD_TYPE)
在PREFERRED_FOOD表中,这将确保PREFERRED_FOOD表中的每个PREFFOOD都已存在于FOOD表的FOOD_TYPE中。并且在FOOD表中使用,现在它非常明显。
FOREIGN KEY (ANIMAL_TYPE) REFERENCES ANIMALS (ANIMAL_TYPE)
答案 2 :(得分:0)
根据您使用的DBMS(请编辑您的问题以包含此内容),您可能希望在ANIMAL_TYPE
和PREFERED_FOOD
列上创建唯一约束。
这样的事情:
ALTER TABLE PREFERED_FOOD
ADD CONSTRAINT uc_FoodAnimal UNIQUE (ANIMAL_TYPE,PREFERED_FOOD)
答案 3 :(得分:0)
坦率地说,我在遵守您的要求时遇到了一些麻烦,但是代表动物及其食物的简单模型可能看起来像这样:
SPECIES_FOOD列出了给定物种可以吃的所有食物,然后INDIVIDUAL只通过PREFERRED_FOOD_NAME字段选择其中一种食物。
由于INDIVIDUAL.SPECIES_NAME是两者物种和物种_FFO的FK,个人永远不会喜欢其物种无法食用的食物。
这当然假设个体动物不能有一种以上的首选食物。 1 它也假设它没有 - 如果不是这样的话,只需制作INDIVIDUAL.PREFERRED_FOOD_NAME不是。
INDIVIDUAL_NAME故意不做了一把钥匙,所以你可以说两只名为" Felix"的猫。如果这不合适,您可以轻松添加相应的密钥。
如果您需要了解食物的名称,并且您不需要独立于任何物种代表食物,则可以完全省略食物表。
1 如果每只动物可以有多种喜欢的食物,那么你需要再多一个餐桌"在"之间。个人和SPECIES_FOOD,并谨慎使用识别关系,因此SPECIES_NAME一直向下迁移(以防止更喜欢该物种不能食用的食物)。
答案 4 :(得分:0)
如果您采用ANIMALS和PREFERRED_FOOD的(自然)JOIN,那么您将得到一张表格,其中列出了每只动物的类型和首选食物。
您希望该组合对每只动物都“有效”,其中“有效”的意思是“出现在FOOD中列出的有效动物类型/食物类型组合的列举中。
所以你有一个类似于FK的约束,但这次“外键”不出现在基表中,而是出现在两个表的连接中。对于这种类型的约束,SQL语言具有CHECK约束和ASSERTIONS。
ASSERTION版本是最简单的。这是一个约束(我对属性名称有些自由,以避免仅仅混淆了点的属性重命名)
CREATE ASSERTION <name for your constraint here>
CHECK NOT EXISTS (SELECT ANIMAL_TYPE, FOOD_TYPE
FROM ANIMALS NATURAL JOIN PREF_FOOD
WHERE (ANIMAL_TYPE, FOOD_TYPE) NOT IN
SELECT ANIMAL_TYPE, FOOD_TYPE FROM FOOD_TYPE);
但是你的普通SQL引擎不支持ASSERTION。所以你必须使用CHECK约束。例如,对于PREF_FOOD表,您需要的CHECK约束可能类似于
CHECK EXISTS (SELECT 1
FROM FOOD NATURAL JOIN ANIMAL
WHERE ANIMAL_TYPE = <animal type of inserted row> AND
FOOD_TYPE = <food type of inserted row>);
理论上,这应该足以强制执行您的约束,但是再一次,您的普通SQL引擎将再次不支持这种CHECK约束,因为除了定义约束之外的表的引用。
所以你有的选择是采用相当复杂的(*)设置,如catcall,或使用触发器强制执行约束(你必须写很多这些(至少三个或六个,没有)通过详细考虑这一点,你的下一个最佳选择是在应用程序代码中强制执行此操作,并且将再次有三个或六个(或多或少)不同的地方需要执行相同数量的不同检查。
在所有这三个场景中,您最好想要在其他地方记录约束的存在,以及它的确切含义。对于阅读此设计的第三方来说,三者中的任何一个都不会让这一点变得非常明显。
(*)“复杂”可能不是正确的词,但请注意,此类解决方案依赖于故意冗余,因此故意低于3NF的设计。这意味着您的设计暴露于更新异常,这意味着用户更难以更新数据库并使其保持一致(正是因为故意的冗余)。