【问题标题】:How to Delete all data from a table which contain self referencing foreign key如何从包含自引用外键的表中删除所有数据
【发布时间】:2010-03-20 13:22:00
【问题描述】:

我有一个表,其中定义了员工关系。 即

EmpID   Name  SeniorId
-----------------------
1         A      NULL
2         B       1
3         C       1
4         D       3

等等……

Senior ID 是主键表与引用列 EmpId 相同的外键 我想清除该表中的所有行而不删除任何约束。我该怎么做?

需要这样删除 4、3、2、1

我该怎么做

编辑:

Jhonny 的答案对我有用,但哪个答案更有效。

【问题讨论】:

  • 很明显,Jhonny's 是最有效和最好的答案,我将留下我的答案只是为了提醒一下,有时我并没有我想的那么聪明。
  • @Larry。根据它应该会降低效率,因为它会先更新所有行然后删除它们。而不是直接删除。哪个操作更有效,循环还是查询?请让我清楚。我不知道这个
  • 两个基于集合的命令可能比在循环中运行大量基于集合的命令更快,即使后一种情况下的每个命令影响的行数较少。实际结果可能会因表中的索引或参考水平的数量而异;您必须进行测试以确保您拥有真正的最佳情况。但是 Jhonny 的“看起来”更好,而且肯定更清晰,更容易实现。
  • @Larry:感谢您的支持。谢谢解释。

标签: sql sql-server database tsql


【解决方案1】:

我不知道我是否遗漏了什么,但也许你可以试试这个。

UPDATE employee SET SeniorID = NULL
DELETE FROM employee

【讨论】:

  • 嘿嘿。把事情简单化! :-)
  • 我觉得这个答案是一些深刻的人生教训:)
  • 是的,日复一日,我越来越接近这样一个想法,即在思考自己之前,我们正在搜索网络以查看其他人的思考结果......顺便说一句很好的答案:D
【解决方案2】:

如果表非常大(数百万的基数),并且不需要记录 DELETE 事务,则删除约束和 TRUNCATE 并重新创建约束是迄今为止最有效的方法。此外,如果其他表中有外键(在这个特定的表设计中似乎是这样),那么在所有情况下都必须首先删除这些行。

规范化对递归/分层/树关系一无所知,所以我认为这是您对 DVK 将其拆分为自己的表的建议的回复中的一个红鲱鱼 - 已经对该表进行垂直分区当然是可行的并且还要考虑您是否可以利用它来获得我在下面列出的任何其他好处。正如 DVK 所暗示的,在这个特定的设计中,我经常看到一个单独的链接表来记录自我关系和其他类型的关系。这有很多好处:

  • 多对多上下而不是多对一(不常见,但可能有用)
  • 使用关系和关系类型表中的行而不是员工表中的新列来跟踪不同类型的直接关系 - 经理、导师、助理、工资审批人、费用审批人、技术报告对象
  • 通过在关系行上包含活动指示符和生效日期,以时间一致的方式(包括终止的员工层次结构历史)跟踪不断变化的层次结构 - 这只有在将关系规范化到自己的表中时才完全可能
  • SeniorID 中没有 NULL(实际上在任一 ID 上) - 这是避免错误逻辑的明显优势,但是当您必须离开连接到关系表时,NULL 通常会出现在视图中
  • 一种更好的专用索引策略 - 相对于将 SeniorID 添加到您已经在 Employee 上拥有的选定索引(尤其是随着关系类型数量的增加)

当然,与此关系相关的信息越多,就越强烈地表明该关系本身值得一个表(即,它是关系数据库中使用的词的真正意义上的“关系” - 相关数据存储在关系或表中 - 与主键相关),因此关系的正常形式可能强烈表明创建关系表而不是员工表中的简单外键关系。

好处还包括其简单的删除方案:

DELETE FROM EmployeeRelationships;
DELETE FROM Employee;

您会注意到这里与 SO 上已接受的答案有着惊人的等价性,因为在您的情况下,没有高级关系的员工有一个 NULL - 所以在那个答案中,海报首先将全部设置为 NULL 以消除关系,然后删除员工。

TRUNCATE 的使用可能适当,具体取决于约束(EmpployeeRelationships 通常能够被 TRUNCATEd,因为它的主键通常是复合键,而不是任何其他表中的外键)。

【讨论】:

    【解决方案3】:

    试试这个

    DELETE FROM employee;
    

    【讨论】:

    • 这通常不起作用。考虑一个表 foo,其中有一列 id 和另一列 parent_id,它是 id 列上的外键。如果您有两条记录 A: [id = 1, parent_id = 0] 和 B: [id = 2, parent_id = 1],尝试同时删除两条记录会失败,如果在删除 B 之前尝试删除 A(因为 B 持有对 A 的约束)。接受的答案是最好的方法 - 将所有相关记录的 parent_id 设置为 NULL,然后删除。
    • 如果是这样,那么编写 DBMS 的人应该学习一些有关关系模型的东西。 “每当执行一条 SQL 语句时,在对 SQL 数据和由该执行产生的模式进行任何更改后的某个时间点,都会检查其模式为立即的每个约束,以查看它是否得到满足。”正如 SQL 标准所拥有的那样。更新是设置级别的,并且必须在“更改已生效”之后进行约束检查。
    【解决方案4】:

    在循环中,运行一个命令,删除所有具有未引用 EmpID 的行,直到剩下零行。有多种方法可以编写内部 DELETE 命令:

     DELETE FROM employee WHERE EmpID NOT IN (SELECT SeniorID FROM employee)
    
     DELETE FROM employee e1 WHERE NOT EXISTS
        (SELECT * FROM employee e2 WHERE e2.SeniorID = e.EmpID
    

    可能还有第三个使用 JOIN,但我不熟悉 SQL Server 的语法。

    【讨论】:

    • @Larry:谢谢回答。我认为这个答案只考虑了两个级别的高级。请查看我在 DVK 的回答中的评论了解更多详情
    • 这就是为什么你必须在一个循环中执行它直到没有剩余的行。但 Jhonny 的回答更好。
    【解决方案5】:

    一种解决方案是通过将“高级”关系拆分到单独的表中来规范这一点。为方便起见,将第二个表设为“empID1|empID2|relationship_type”。

    除此之外,您需要循环执行此操作。一种方法是这样做:

    declare @count int
    select @count=count(1) from table
    while (@count > 0)
    BEGIN
        delete employee WHERE NOT EXISTS
           (select 1 from employee 'e_senior' 
            where employee.EmpID=e_senior.SeniorID)
        select @count=count(1) from table
    END
    

    【讨论】:

    • 组织结构未定义,因此我们无法规范化。甚至1个高级也可以有它的高级,依此类推。即A的前辈B、B的前辈C、C的前辈D等等
    • 我不知道为什么不明白,反正我添加了第二种方法
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-17
    • 2022-01-09
    • 1970-01-01
    相关资源
    最近更新 更多