38

什么时候可以在数据库中接受循环引用?

理论和实践,任何帮助表示赞赏。

4

8 回答 8

37

考虑城市和州。每个城市都存在于一个州内。每个州都有一个首都。

CREATE TABLE city (
  city  VARCHAR(32),
  state VARCHAR(32) NOT NULL,
  PRIMARY KEY (city),
  FOREIGN KEY (state) REFERENCES state (state)
);

CREATE TABLE state (
  state VARCHAR(32),
  capital_city VARCHAR(32),
  PRIMARY KEY (state),
  FOREIGN KEY (capital_city) REFERENCES city (city)
);

第一个问题 - 您不能如图所示创建这些表,因为外键不能引用表中不(尚)存在的列。解决方案是在没有外键的情况下创建它们,然后再添加外键。

第二个问题 - 您不能在任一表中插入行,因为每个插入都需要另一个表中预先存在的行。解决方案是将其中一个外键列设置为 NULL,然后分两个阶段插入该数据。例如

--Create state record
INSERT INTO state (state, capital_city) VALUES ('Florida', NULL);

--Create various city records
INSERT INTO city (city, state) VALUES ('Miami', 'Florida');
INSERT INTO city (city, state) VALUES ('Tallahassee', 'Florida');
INSERT INTO city (city, state) VALUES ('Orlando', 'Florida');

--Set one of the cities as the capital
UPDATE state SET capital_city = 'Tallahassee' WHERE state = 'Florida';
于 2012-07-20T10:35:38.137 回答
22

指向其他记录的记录在数据库中很有用。有时这些记录形成一个循环。这可能仍然有用。实践中唯一真正的烦恼是避免违反约束。

例如,如果您有一个用户和事务表,用户可能有一个指向他的最后一个事务的指针。您需要先插入事务,然后将其更新last_transaction_id为正确的值。虽然这两个记录都存在,但您无法删除它们,因为user.last_transaction_id指向transaction.idtransaction.user_id指向user.id。这意味着没有交易的用户有一个 null last_transaction_id。这也意味着您必须先将该字段设为空,然后才能删除交易。

管理这些外键约束是一件痛苦的事,但它肯定是可能的。如果您稍后将约束添加到数据库,从而引入新的循环依赖项,则可能会出现问题。在这种情况下你必须小心。但是,只要循环中的一条记录具有可为空的外键字段,就可以打破循环并删除记录。只要您以正确的顺序插入记录,更新通常不会成为问题。

于 2009-06-17T14:23:44.777 回答
5

这在技术上是可行的,但在删除记录时会导致各种问题,因为它会产生鸡与蛋的问题。这些问题通常会采取激烈的措施,例如手动删除 FK 并删除有问题的项目来解决。

如果你有这样的关系:

create table foo_master (
       foo_master_id int not null primary key
      ,current_foo_id int
)


create table foo_detail (
       foo_detail_id int not null primary key
       foo_master_id int not null
)

alter table foo_master
  add constraint fk_foo_current_detail
      foreign key (current_foo_id)
      references foo_detail

alter table foo_detail
  add constraint fk_foo_master
      foreign key (foo_master_id)
      references foo_master

然后由于循环依赖关系,删除记录可能会导致这样的鸡和聚集问题。

一个更好的模式看起来像:

create table foo_master (
       foo_master_id int not null primary key
)


create table foo_detail (
       foo_detail_id int not null primary key
       foo_master_id int not null
       is_current char (1)
)

alter table foo_detail
  add constraint fk_foo_master
      foreign key (foo_master_id)
      references foo_master

这意味着该关系是非循环的,并且仍然可以识别“当前” foo_detail 记录。

于 2009-06-17T14:15:23.547 回答
5

Oracle 分层查询语法的最新添加之一 -NOCYCLE关键字 - 明确用于此目的 - 处理数据中的循环引用。我看不出有什么问题,之前也不得不处理这种模型。这并不太难,尤其是在支持可延迟约束的 Oracle 中。

于 2009-06-18T02:17:58.123 回答
3

我已经看到出于性能原因进行了循环引用。虽然它看起来很丑,性能可能可以忽略不计。

示例:一些公告板(我认为 phpBB 会这样做)在类别表中有一个 lastpostid,它是线程中最后一个帖子的快捷方式。

这将创建一个圆圈,其中最后一个帖子有一个指向类别表的 FK,而类别表有一个指向最后一个帖子的 FK。

就像我说的,我不是很喜欢它,但我已经看到它完成了。

于 2009-06-17T13:18:39.497 回答
0

应该像瘟疫一样避免循环引用。可以建立双向关系,甚至与自己的关系(如果你是一张桌子),但循环依赖只是自找麻烦。

于 2009-06-17T13:16:03.480 回答
0

我很少遇到必要的 1:1 关系并强加循环关系

请注意,这种关系中的外键字段必须可以为空,否则您永远无法从表中删除行

于 2009-06-17T13:28:04.220 回答
0

如果您使用的是只写数据库,我想这不是问题。如果您计划使用 CRUD 的 RUD 部分,您可能会在处理它们时遇到(通常可以避免的)复杂问题。

于 2009-06-17T13:59:11.487 回答