SQL删除孤儿

时间:2010-01-08 08:24:19

标签: sql oracle delete-row

假设所有外键都有适当的约束,是否有一个简单的SQL语句来删除数据库中没有引用的行?

delete from the_table一样简单,只是跳过包含子记录的任何行?

我试图避免手动循环遍历表格或添加类似where the_SK not in (a,b,c,d)的内容。

4 个答案:

答案 0 :(得分:6)

您可以在10g中使用包含错误记录的扩展DELETE语句。

首先使用DBMS_ERRLOG创建一个日志记录表(它只是原始表的副本,带有一些额外的前缀列:ORA_ERR_MESG$, ..., ORA_ERR_TAG$

execute dbms_errlog.create_error_log('parent', 'parent_errlog');

现在,您可以使用delete语句的LOG ERRORS子句捕获具有现有完整性约束的所有行:

delete from parent
   log errors into parent_errlog ('holding-breath')
   reject limit unlimited;

在这种情况下,“持有呼吸”评论将进入ORA_ERR_TAG$列。

您可以阅读完整文档here

如果父表很大并且您只想删除一些杂散行,那么最终会得到一个parent_errlog表,它基本上与您的parent表重复。如果这不行,你将不得不做很多事情:

  1. 直接引用子表(在Tony's solution之后),或
  2. 在PL / SQL中循环遍历表并捕获任何异常(在Confusion'sBob's solutions之后)。

答案 1 :(得分:2)

最简单的方法可能是编写一个尝试逐个删除表中行的应用程序或存储过程,并简单地忽略由于外键约束导致的失败。之后,应删除不在外键约束下的所有行。根据所需/可能的性能,这可能是一种选择。

答案 2 :(得分:1)

没有。显然你可以做到这一点(但我意识到你不愿意):

delete parent
where  not exists (select null from child1 where child1.parent_id = parent.parent_id)
and    not exists (select null from child2 where child2.parent_id = parent.parent_id)
...
and    not exists (select null from childn where childn.parent_id = parent.parent_id);

答案 3 :(得分:1)

执行此操作的一种方法是编写如下内容:

eForeign_key_violation EXCEPTION;
PRAGMA EXCEPTION_INIT(eForeign_key_violation, -2292);

FOR aRow IN (SELECT primary_key_field FROM A_TABLE) LOOP
  BEGIN
    DELETE FROM A_TABLE A
    WHERE A.PRIMARY_KEY_FIELD = aRow.PRIMARY_KEY_FIELD;
  EXCEPTION
    WHEN eForeign_key_violation THEN
      NULL;  -- ignore the error
  END;
END LOOP;

如果存在子行,DELETE将失败,并且不会删除任何行,您可以继续使用下一个键。

请注意,如果您的表格较大,可能需要一段时间。