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

时间:2017-11-10 15:39:06

标签: sql oracle

大约有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;

3 个答案:

答案 0 :(得分:1)

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

  1. 将引用表Employee的所有外键设置为“ON DELETE CASCADE”(请参阅​​this link for example
  2. delete from Employee where <all your conditions>;
  3. 当FK被设置为“ON DELETE CASCADE”时,Oracle将在父表中删除行时自动删除子表中的孤立行。

答案 1 :(得分:0)

如果您在删除过程中发现任何问题但仍希望在没有光标的情况下进行整个操作,则可以使用 DML Error Logging

  

在某些情况下,问题最明显的解决方案是DML语句(INSERT ... SELECT,UPDATE,DELETE),但您可以选择避免使用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;

答案 2 :(得分:0)

假设您想要保持数据的完整性,并且当从一个表中删除时出现错误,那么您希望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每位员工。