2010-01-08 123 views
3

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

delete from the_table那样简单,只需跳过任何带有子记录的行?

我试图避免手动循环表或添加类似where the_SK not in (a,b,c,d)

回答

6

您可能能够使用扩展DELETE声明在10g中包含错误日志。

首先使用DBMS_ERRLOG创建一个记录表(这仅仅是一个带有一些额外的前缀列原始表的副本:ORA_ERR_MESG$, ..., ORA_ERR_TAG$)现在

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

,你可以使用delete语句的错误日志条款捕捉那些现有的完整性约束的所有行:

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)。
+0

不错,我不知道这个错误记录。这样我既不需要指定孩子也不需要遍历行,但我认为你应该添加“拒绝限制无限”(或任何数字)。事实上,删除仍然失败。 – RichN 2010-01-12 07:56:19

+0

斑点 - 添加到我的答案 – 2010-01-12 13:53:45

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); 
2

最简单的方法可能是编写一个应用程序或存储过程,该应用程序或存储过程试图一次一个地删除表中的行,并简单地忽略由于外键约束而导致的故障。之后,所有不在外键约束下的行都应该被删除。根据所需/可能的性能,这可能是一个选项。要做到这一点

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将失败,没有行会被删除,你可以继续你的下一个重点。

请注意,如果您的表格很大,则可能需要很长时间。