如何表示部分可以为空的外键?

时间:2015-10-27 21:36:15

标签: sql oracle foreign-keys composite-key

假设您要在数据库中存储联系电话号码,人员和住户。每个人都属于一个家庭。电话号码可以与家庭中的特定个人相关联,或者可以是家庭的一般号码。这些关系部分表示在以下Oracle SQL中:

CREATE TABLE HOUSEHOLD (
    HOUSEHOLD_ID INTEGER PRIMARY KEY
);

CREATE TABLE PERSON (
    PERSON_ID INTEGER PRIMARY KEY,
    HOUSEHOLD_ID INTEGER NOT NULL,
    CONSTRAINT FK_PERSON_HOUSEHOLD
        FOREIGN KEY (HOUSEHOLD_ID)
        REFERENCES HOUSEHOLD (HOUSEHOLD_ID)
);

CREATE TABLE CONTACT_PHONE (
    PHONE_NUMBER CHAR(10) PRIMARY KEY,
    HOUSEHOLD_ID INTEGER NOT NULL,
    PERSON_ID INTEGER NULL,
    CONSTRAINT FK_PHONE_HOUSEHOLD
        FOREIGN KEY (HOUSEHOLD_ID)
        REFERENCES HOUSEHOLD (HOUSEHOLD_ID),
    CONSTRAINT FK_PHONE_PERSON
        FOREIGN KEY (PERSON_ID)
        REFERENCES PERSON (PERSON_ID)
);

外键和NULL / NOT NULL约束确保每个人都属于一个家庭,每个联系电话只与一个家庭相关联,并且联系电话可能与人有关,也可能没有。他们没有阻止的一件事是与一个家庭相关联的电话号码,以及与属于不同家庭的人相关联的电话号码。是否有使用数据库约束表达此类关系的标准方法?给出的示例适用于Oracle,但也欢迎其他数据库平台的解决方案。

1 个答案:

答案 0 :(得分:0)

我们希望PERSON表的HOUSEHOLD_ID和PERSON_ID列具有外键,但是如果CONTACT_PHONE表的PERSON_ID列为NULL,则不希望检查它。解决方案是创建一个复制HOUSEHOLD_ID的虚拟/计算列,但仅当PERSON_ID不为NULL时,才在外键中使用它而不是HOUSEHOLD_ID:

CREATE TABLE CONTACT_PHONE (
    PHONE_NUMBER CHAR(10) PRIMARY KEY,
    HOUSEHOLD_ID INTEGER NOT NULL,
    PERSON_ID INTEGER NULL,
    PERSON_HOUSEHOLD_ID GENERATED ALWAYS AS (
        CAST(DECODE(PERSON_ID, NULL, NULL, HOUSEHOLD_ID) AS INTEGER)
    ) VIRTUAL,
    CONSTRAINT FK_PHONE_HOUSEHOLD
        FOREIGN KEY (HOUSEHOLD_ID)
        REFERENCES HOUSEHOLD (HOUSEHOLD_ID),
    CONSTRAINT FK_PHONE_PERSON
        FOREIGN KEY (PERSON_HOUSEHOLD_ID, PERSON_ID)
        REFERENCES PERSON (HOUSEHOLD_ID, PERSON_ID)
);

这样,当PERSON_ID不为NULL时,PERSON_HOUSEHOLD_ID将与HOUSEHOLD_ID相同,并且将正常检查FK_PHONE_PERSON。

但是,当PERSON_ID为NULL时,PERSON_HOUSEHOLD_ID也将为NULL。由于参与FK_PHONE_PERSON的两个本地列都是NULL,因此不会检查约束。