【问题标题】:Dealing with circular reference when entering data in SQL在 SQL 中输入数据时处理循环引用
【发布时间】:2012-03-20 06:46:39
【问题描述】:

您使用什么样的 sql 技巧将数据输入到两个表中,并在两者之间进行循环引用。

Employees
    EmployeeID <PK>
    DepartmentID <FK> NOT NULL

Departments
    DepartmentID <PK>
    EmployeeID <FK> NOT NULL

员工属于一个部门,一个部门必须有一个经理(部门主管)。

我是否必须禁用约束才能进行插入?

【问题讨论】:

  • 这对我来说似乎是一个奇怪的模式,你能解释一下为什么你有这种关系吗?
  • 您部门表中的 EmployeeId 是像部门经理一样吗?
  • 在 Oracle 中,“技巧”是定义外键 DEFERRABLE。

标签: sql


【解决方案1】:

我假设您的 Departments.EmployeeID 是部门主管。我要做的是使该列可以为空;那么你可以先创建部门,再创建员工。

【讨论】:

  • 没有理由不能在可空字段上使用外键。
  • Chaos:当然这是一种选择。比尔:想法是每个部门都必须有一个部门负责人。
  • 还可以将Employees.DepartmentID 设为可为空。这就像“鸡或蛋”。但架构仍然有味道。
【解决方案2】:

问:我是否必须禁用约束才能进行插入?
答:在 Oracle 中,不需要,如果外键约束是 @987654324 @(见下例)

对于甲骨文:

设置所有延迟的约束; 插入部门值('foo','dummy'); 插入员工值('bar','foo'); UPDATE Departments SET EmployeeID = 'bar' WHERE DepartmentID = 'foo'; 犯罪;

让我们解开它:

  • (必须关闭自动提交)
  • 延迟执行外键约束
  • 在 Department 表中插入一行,其中 FK 列具有“虚拟”值
  • 使用对部门的 FK 引用向 Employee 表插入一行
  • 将部门 FK 中的“虚拟”值替换为真实参考
  • 重新启用约束的实施

注意:禁用外键约束对所有会话生效,DEFERRING 约束是在事务级别(如示例中)或会话级别 (ALTER SESSION SET CONSTRAINTS=DEFERRED;)

Oracle 已允许将外键约束定义为 DEFERRABLE 至少十年。我将所有外键约束(当然)定义为 DEFERRABLE INITIALLY IMMEDIATE。这保持了每个人都期望的默认行为,但允许在不需要禁用外键的情况下进行操作。

见 AskTom:http://www.oracle.com/technology/oramag/oracle/03-nov/o63asktom.html

见 AskTom:http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:10954765239682

另见:http://www.idevelopment.info/data/Oracle/DBA_tips/Database_Administration/DBA_12.shtml

[编辑]

答:在 Microsoft SQL Server 中,您不能像在 Oracle 中那样延迟外键约束。禁用和重新启用外键约束是一种方法,但我对 1)性能影响(重新启用约束时检查整个表的外键约束)的前景感到不寒而栗,2)处理异常,如果(何时?)重新启用约束失败。请注意,禁用约束将影响所有会话,因此当禁用约束时,其他会话可能会插入和更新行,这将导致重新启用约束失败。

使用 SQL Server,更好的方法是删除 NOT NULL 约束,并在插入/更新行时允许 NULL 作为临时占位符。

对于 SQL Server:

--(从 Departments.EmployeeID 中删除 NOT NULL 约束) 插入部门值('foo',NULL) 去 插入员工值('bar','foo') 去 更新部门设置 EmployeeID = 'bar' where DepartmentID = 'foo' 去

[/编辑]

【讨论】:

  • 刚刚发现 Sybase 也允许您通过 SET OPTION WAIT_FOR_COMMIT = 'ON' 执行此操作。将其设置为 ON 将延迟参照检查,直到您调用 COMMIT。设置选项 WAIT_FOR_COMMIT = 'ON' ;插入员工……插入部门……提交;设置选项 WAIT_FOR_COMMIT = 'OFF' ;现在只缺少 MSSQL ;)
  • -1 “更好的方法是删除 NOT NULL 约束”——如何防止“临时占位符”无限期保留。
  • @Tom:感谢关于在 Sybase 上启用“延迟约束检查”的有用说明。
【解决方案3】:

这个问题可以通过可延迟的约束来解决。提交整个事务时会检查此类约束,从而允许您在同一事务中插入员工和部门,并相互引用。 (假设数据模型有意义)

【讨论】:

  • 听起来像 Spencer7593 描述的方法。有人知道如何在 MSSQL 中做到这一点吗?
【解决方案4】:

通过删除循环引用重构架构。
从任一表架构中删除一个 ID 列。

Departments.EmployeeID 在我看来似乎不属于那里。

【讨论】:

    【解决方案5】:

    我想不出一种非 hackish 的方式来做到这一点。我认为您将需要删除约束或执行某种类型的愚蠢虚拟值,这些值会在所有插入后更新。

    我建议重构数据库架构。我想不出您为什么希望它以这种方式工作的任何原因。

    也许像 Employee、EmployeeDepartment (EmployeeId, DepartmentId) 和 Department 这样的东西会是实现相同目标的更好方法。

    【讨论】:

      【解决方案6】:

      您可以在 Department 表中为“未分配”创建一行

      要创建一个包含新员工的新部门

      1. 在“未分配”部门中创建员工 (EmployeeA)
      2. 使用员工 EmployeeA 创建新部门(DepartmentA)
      3. 将 EmployeeA 更新到 DepartmentA 中

      这不会使您当前的架构无效,您可以设置一个定期运行的任务来检查未分配部门的成员。

      您还需要创建一个默认员工作为未分配的员工

      编辑:

      虽然chaos提出的解决方案要简单得多

      【讨论】:

        【解决方案7】:

        我使用过一些不错的设计。所有这些都涉及从 Department 表中删除“经理”EmployeeID 并从 Employee 表中删除 DepartmentID。我看过几个提到它的答案,但我会澄清我们是如何使用它的:

        我通常最终得到一个 EmployeeDepartment 关系链接表 - 多对多,通常带有 IsManager、IsPrimaryManager、IsAdmin、IsBackupManager 等标志,这些标志阐明了关系 有些可能受到限制,因此每个人只允许一个主经理部门(尽管一个人可以是多个部门的 PrimaryManager)。如果您不喜欢单个表,则可以有多个表:EmployeeDepartment、ManagerDepartment 等,但您可能会遇到一个人是经理但不是员工等情况。

        我们通常还允许人们成为多个部门的成员。

        为了简化访问,您可以提供适当执行连接的视图。

        【讨论】:

          【解决方案8】:

          是的,在这种情况下,您必须禁用外键。

          【讨论】:

            【解决方案9】:

            您需要永久删除一个或另一个引用。这不是一个可行的设计结构。必须先输入哪个?部门还是员工?除非您的部门都是一个大员工,否则这种结构无论如何都没有意义,因为每个员工都必须有一个不同的部门 ID。

            【讨论】:

            • 我不明白为什么这不是一个可行的设计结构。员工属于一个部门,一个部门必须有一个经理(部门主管)。
            • 没有一个部门不一定要有经理。部门主管职位空缺。但它是不可行的,因为你永远不可能有一个循环的 pk/fk 情况并使其与黑客一起工作以绕过限制。破解限制的需要是你设计糟糕的第一个线索。我将有一个员工表,它是父表,一个包含组织结构的组织表,然后是一个分配表,其中包含人员分配到的结构和层次结构(谁在这里报告)。两者都有 FK。
            猜你喜欢
            • 2016-09-04
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-04-13
            • 1970-01-01
            • 2023-03-29
            • 2015-11-08
            • 1970-01-01
            相关资源
            最近更新 更多