双向外键约束

时间:2009-10-22 15:10:56

标签: sql-server database foreign-keys

我正在考虑设计类似于以下内容的数据库模式:

Person (
  PersonID int primary key,
  PrimaryAddressID int not null,
  ...
)

Address (
  AddressID int primary key,
  PersonID int not null,
  ...
)

Person.PrimaryAddressID和Address.PersonID将是相应表的外键。

显而易见的问题是,无法在任何一个表中插入任何内容。有没有办法设计一个工作模式来强制每个具有主要地址的人员?

6 个答案:

答案 0 :(得分:11)

“我认为这是不可能的。在知道人员的身份证之前,您无法创建地址记录,并且在知道PrimaryAddressId字段的AddressId之前无法插入人员记录。”

从表面上看,这种主张似乎很有吸引力。但是,这很有道理。

这是一个非常常见的问题,SQL DBMS供应商已经尝试攻击了几十年了。

关键是所有约束检查必须“延迟”,直到两个插入完成。这可以通过不同的形式实现。数据库事务可以提供类似“设置延迟约束检查”的操作的可能性,并且您已完成(如果不是因为在这个特定示例中,您可能必须非常努力地将您的设计搞乱能够只定义两个FK约束,因为其中一个只是在SQL意义上不是'真正的'FK!)。

此处所述的基于触发器的解决方案实现了基本相同的效果,但这些解决方案暴露于应用程序强制完整性所存在的所有维护问题。

在他们的作品中,Chris Date& Hugh Darwen描述了什么是这个问题的真正解决方案:多重任务。也就是说,基本上,可以组成几个不同的更新语句,并让DBMS对其进行操作,就好像这是一个单一的语句。这个概念的实现确实存在,但你找不到任何谈论SQL的东西。

答案 1 :(得分:2)

我们在地址表中标记主要地址,然后使用仅强制执行每人记录的触发器(但必须有一条记录)。如果更改主地址,它将更新旧的主地址以及新的主地址。如果删除主地址并且存在其他地址,则会将其中一个(基于一系列规则)提升为主地址。如果插入了地址并且插入了第一个地址,则会自动将该地址标记为主地址。

答案 2 :(得分:2)

这是多对多关系的完美范例。要解决这个问题,您应该拥有中间PERSON_ADDRESS表。换句话说;

PERSON table
person_id (PK)

ADDRESS table
address_id (PK)

PERSON_ADDRESS
person_id (FK) <= PERSON
address_id (FK) <= ADDRESS
is_primary (BOOLEAN - Y/N)

通过这种方式,您可以为PERSON分配多个地址,并在多个PERSON中重复使用ADDRESS记录(适用于家庭成员,同一公司的员工等)。使用PERSON_ADDRESS表中的is_primary字段,您可以识别该person_addrees组合是否是某人的主要地址。

答案 3 :(得分:0)

第二个FK(PersonId from Address to Person)限制性太强,恕我直言。你是建议一个地址只能有一个人吗?

答案 4 :(得分:0)

从您的设计来看,地址似乎只适用于一个人,因此只需使用PersonID作为地址表的键,然后删除AddressID键字段。

答案 5 :(得分:-1)

我知道我可能会被钉在十字架上或者其他什么,但是这里......

我这样做是因为我的“特别非常独特和非标准”的业务需求(=(即使我说话,上帝我开始听起来像SQL DDL)。

这是一个例子:

CREATE TABLE IF NOT EXISTS PERSON(
    ID INT, 
    CONSTRAINT PRIMARY KEY (ID), 
    ADDRESS_ID INT NOT NULL DEFAULT 1, 
    DESCRIPTION VARCHAR(255), 
    CONSTRAINT PERSON_UQ UNIQUE KEY (ADDRESS_ID, ...));

INSERT INTO PERSON(ID, DESCRIPTION) 
    VALUES (1, 'GOVERNMENT');

CREATE TABLE IF NOT EXISTS ADDRESS(
    ID INT, 
    CONSTRAINT PRIMARY KEY (ID), 
    PERSON_ID INT NOT NULL DEFAULT 1, 
    DESCRIPTION VARCHAR(255), 
    CONSTRAINT ADDRESS_UQ UNIQUE KEY (PERSON_ID, ...), 
    CONSTRAINT ADDRESS_PERSON_FK FOREIGN KEY (PERSON_ID) REFERENCES PERSON(ID));

INSERT INTO ADDRESS(ID, DESCRIPTION) 
    VALUES (1, 'ABANDONED HOUSE AT THIS ADDRESS');

ALTER TABLE PERSON ADD CONSTRAINT PERSON_ADDRESS_FK FOREIGN KEY (ADDRESS_ID) REFERENCES ADDRESS(ID);

&lt; ...生活还在继续......无论你是否为这个人提供和解决,反之亦然&gt;

我定义了一个表,然后另一个表引用第一个表,然后更改第一个表以反映对第二个表的引用(在第一个表创建时不存在)。它不适用于特定的数据库;如果我需要它我只是尝试它,如果它工作,然后我使用它,如果没有那么我尽量避免在设计中有这种需要(我不能总是控制它,有时设计是按原样交给我) 。如果你有一个没有人的地址,那么它属于“政府”人。如果你有一个“无家可归者”,那么它就会获得“废弃的房子”地址。我运行一个流程来确定哪些房屋没有用户