7

举个愚蠢的例子:我有很多家畜,每一个都有一个名字作为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
)

(例如,狗吃骨头和肉,猫吃鱼和牛奶)

我的问题来了:我想在 PREFERED_FOOD 中添加一个外部约束,因为 PREF_FOOD 是来自 FOOD 的 FOOD_TYPE,其中 FOOD.ANIMAL_TYPE=ANIMALS.TYPE。如何在不复制 PREFERED_FOOD 上的 ANIMAL_TYPE 的情况下定义这个外键?

我不是 SQL 专家,所以如果它真的很容易,你可以称我为愚蠢 ;-)

4

5 回答 5

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)
);
于 2012-09-25T00:37:47.950 回答
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)
于 2012-09-24T21:25:17.863 回答
0

根据您使用的 DBMS(请编辑您的问题以包含此内容),您可能希望在ANIMAL_TYPEandPREFERED_FOOD列上创建唯一约束。

像这样的东西:

ALTER TABLE PREFERED_FOOD
ADD CONSTRAINT uc_FoodAnimal UNIQUE (ANIMAL_TYPE,PREFERED_FOOD)
于 2012-09-24T21:27:25.467 回答
0

坦率地说,我在满足您的要求时遇到了一些麻烦,但是代表动物及其食物的简单模型可能如下所示:

在此处输入图像描述

SPECIES_FOOD 列出了给定物种可以吃的所有食物,然后 INDIVIDUAL 通过 PREFERRED_FOOD_NAME 字段选择其中一种。

由于 INDIVIDUAL.SPECIES_NAME 是针对 SPECIES 和 SPECIES_FOOD 的 FK,因此个人永远不会偏爱不能被其物种食用的食物。

这当然是假设个体动物不能有超过一种偏好的食物。1它还假设它可以没有 - 如果不是这种情况,只需将 INDIVIDUAL.PREFERRED_FOOD_NAME 设为 NOT NULL。

INDIVIDUAL_NAME 是故意作为密钥的,因此您可以拥有两只名为“Felix”的猫。如果这不是可取的,您可以轻松添加适当的密钥。

如果您只需要了解食物的名称,并且不需要独立于任何物种来表示食物,则可以完全省略 FOOD 表。


1如果每只动物可能有多种首选食物,您需要在“个体”和 SPECIES_FOOD 之间再增加一张表,并注意继续使用识别关系,因此 SPECIES_NAME 一直向下迁移(以防止首选食物该物种不能食用)。

于 2012-09-25T01:20:03.680 回答
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 引擎不支持断言。所以你必须使用 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。这意味着您的设计会面临更新异常,这意味着用户将更难更新数据库并使其保持一致(正是因为故意冗余)。

于 2012-09-26T13:19:49.200 回答