繁体   English   中英

Oracle基于单个主键删除多个表中的行。

[英]Oracle delete rows in multiple tables based on single primary key.

大约有20个表基于单个主键-EmployeeId进行分支。 我想从数据库中完全淘汰掉大约12,000名员工。 当我删除员工时,其他进程更新这些员工的机会几乎为零。 我打算批量删除它们,然后提交。 从理论上讲,所有删除操作都不应该失败,但是我不确定是否要走光标路线,每500行提交一次还是什么。 这是现在的样子。

--STEP 1: Collect the EmployeeIds to delete in a temp table
Create table temp as select EmployeeId from Employee where <all conditions are met>;

--STEP 2: Delete names
Delete from EmployeeName where EmployeeId in (select EmployeeId from temp);

--STEP 3 - STEP 30: Delete all other child tables
Delete from table inner join another_table on some_key inner join yet_another_table on some_key where EmployeeId in (select EmployeeId from temp);

--STEP 4: Commit
commit;

如果您经常这样做,那么让Oracle为您完成这项工作又如何呢?

  1. 将所有引用表Employee的外键设置为“ ON DELETE CASCADE”( 例如,请参阅此链接
  2. delete from Employee where <all your conditions>;

FK设置为​​“ ON DELETE CASCADE”,当在父表中删除行时,Oracle将自动从子表中删除孤立行。

如果您知道删除过程中遇到的任何麻烦,并且仍然想在没有光标的情况下执行整个操作,则可以使用DML错误记录

在某些情况下,最明显的解决方案是DML语句(INSERT ... SELECT,UPDATE,DELETE),但是由于DML对异常的反应方式,您可以选择避免DML。

默认情况下,当DML语句失败时,将回退整个语句,而不管检测到错误之前成功处理了多少行。

过去,解决此问题的唯一方法是单独处理每一行,最好使用FORALL和SAVE EXCEPTIONS子句进行批量操作。 在Oracle 10g数据库第2版中,引入了DML错误日志记录功能来解决此问题。 在大多数INSERT,UPDATE,MERGE和DELETE语句上添加适当的LOG ERRORS子句,无论错误如何,操作都能完成。

BEGIN
  DBMS_ERRLOG.create_error_log (dml_table_name => 'EmployeeName');
END;
/

Delete from EmployeeName 
where EmployeeId in (select EmployeeId from temp)
LOG ERRORS INTO err$_EmployeeName ('DELETE') REJECT LIMIT UNLIMITED;

假设您要维护数据的完整性,并且从一个表中删除时出错,那么您想要ROLLBACK该员工的所有删除,则可以执行以下操作:

DECLARE
  TYPE Emp_ID_Tab_Type IS TABLE OF Employee.EmployeeID%TYPE;

  All_Employees     Emp_ID_Tab_Type;
  Deleted_Employees Emp_ID_Tab_Type := Emp_ID_Tab_Type();
  Error_Employees   Emp_ID_Tab_Type := Emp_ID_Tab_Type();
BEGIN
  SELECT EmployeeID
  BULK COLLECT INTO All_Employees
  FROM   Employees
  WHERE  1 = 0;    -- Your conditions

  FOR i IN 1 .. All_Employees.COUNT LOOP
    BEGIN
      DELETE FROM child_table1
      WHERE  EmployeeID = All_Employees(i);

      DELETE FROM child_table2
      WHERE  EmployeeID = All_Employees(i);

      -- ...

      DELETE FROM child_table20
      WHERE  EmployeeID = All_Employees(i);

      DELETE FROM Employees
      WHERE  EmployeeID = All_Employees(i);

      COMMIT;
      Deleted_Employees.EXTEND;
      Deleted_Employees(Deleted_Employees.COUNT) := All_Employees(i);
      DBMS_OUTPUT.PUT_LINE( All_Employees(i) || ' deleted' );
    EXCEPTION
      WHEN others THEN
        ROLLBACK;
        Error_Employees.EXTEND;
        Error_Employees(Error_Employees.COUNT) := All_Employees(i);
        DBMS_OUTPUT.PUT_LINE( All_Employees(i) || ' error - ' || SQLERRM );
    END;
  END LOOP;

  -- Do something with the errors
END;

在每个循环结束时使用COMMIT并不是最快的方法,但可以确保您可以ROLLBACK每个员工。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM