3

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

像这样简单的事情delete from the_table只是跳过任何带有子记录的行?

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

4

4 回答 4

6

您也许可以使用DELETE10g 中包含错误日志记录的扩展语句。

首先用于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$列。

您可以在此处阅读完整的文档。

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

  1. 直接引用子表(遵循托尼的解决方案),或者,
  2. 在 PL/SQL 中循环遍历表并捕获任何异常(遵循ConfusionBob 的解决方案)。
于 2010-01-08T10:22:45.533 回答
2

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

于 2010-01-08T10:31:30.400 回答
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);
于 2010-01-08T09:33:18.937 回答
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 将失败并且不会删除任何行,您可以继续执行下一个键。

请注意,如果您的桌子很大,这可能需要很长时间。

于 2010-01-08T12:28:42.060 回答