【问题标题】:Are circular references acceptable in database?数据库中是否可以接受循环引用?
【发布时间】:2010-11-03 15:54:45
【问题描述】:

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

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

【问题讨论】:

标签: database oracle circular-dependency


【解决方案1】:

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

【讨论】:

  • 双向关系是什么意思?记录A指向B和C?与 A 指向 B 和 B 指向 A 相对吗?第二个绝对是圆形的。
【解决方案2】:

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

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

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

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

【讨论】:

    【解决方案3】:

    我很少遇到必要且强制循环关系的 1:1 关系

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

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        【解决方案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 记录。

        【讨论】:

          【解决方案6】:

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

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

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

          【讨论】:

            【解决方案7】:

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

            【讨论】:

              【解决方案8】:

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

              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';
              

              【讨论】:

              • 谢谢。好的例子。 SQL 服务器为此循环引用提供错误 - 外键“FK__city__state__007EABC”引用无效表“状态”。无法创建约束。查看以前的错误。
              • 在不允许 NULL 值的情况下插入行的其他可能性:1. 全部插入,2. 可延迟约束。删除时还有:3. DELESE CASCADE
              • @Falco 您能否指出一个使用 INSERT ALL 和圆形 FK 的(工作)示例?
              • 为什么不能在一个事务中使用两个插入来做到这一点?我的假设是只应在事务结束时检查约束,但显然情况并非如此。
              • 某些数据库(例如 postgresql)允许您使用诸如 SET CONSTRAINTS foo, bar DEFERRED 之类的命令将约束检查推迟到事务结束时
              猜你喜欢
              • 2022-12-11
              • 2022-12-20
              • 1970-01-01
              • 1970-01-01
              • 2012-01-31
              • 1970-01-01
              • 1970-01-01
              • 2015-05-16
              • 1970-01-01
              相关资源
              最近更新 更多