如何表示部分可为空的外键?
How to represent partially nullable foreign keys?
假设您要在数据库中存储联系人 phone 号码、人员和家庭。每个人都属于一个家庭。 phone 号码可能与家庭中的特定个人相关联,也可能是家庭的通用号码。这些关系在以下 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 约束确保每个人都属于一个家庭,每个联系人 phone 都与一个家庭关联,并且联系人 phone可能与人有关,也可能不与人有关。他们没有阻止的一件事是 phone 号码与一个家庭相关联,并且与属于不同家庭的人相关联。有没有一种标准的方法来使用数据库约束来表达这种关系?给出的示例适用于 Oracle,但也欢迎使用适用于其他数据库平台的解决方案。
我们想要一个指向 PERSON table 的 HOUSEHOLD_ID 和 PERSON_ID 列的外键,但如果 PERSON_ID CONTACT_PHONE table 的列为 NULL。解决方案是创建一个复制HOUSEHOLD_ID的virtual/computed列,但仅当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,因此不会检查约束。
假设您要在数据库中存储联系人 phone 号码、人员和家庭。每个人都属于一个家庭。 phone 号码可能与家庭中的特定个人相关联,也可能是家庭的通用号码。这些关系在以下 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 约束确保每个人都属于一个家庭,每个联系人 phone 都与一个家庭关联,并且联系人 phone可能与人有关,也可能不与人有关。他们没有阻止的一件事是 phone 号码与一个家庭相关联,并且与属于不同家庭的人相关联。有没有一种标准的方法来使用数据库约束来表达这种关系?给出的示例适用于 Oracle,但也欢迎使用适用于其他数据库平台的解决方案。
我们想要一个指向 PERSON table 的 HOUSEHOLD_ID 和 PERSON_ID 列的外键,但如果 PERSON_ID CONTACT_PHONE table 的列为 NULL。解决方案是创建一个复制HOUSEHOLD_ID的virtual/computed列,但仅当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,因此不会检查约束。