简体   繁体   English

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

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

There is about 20 tables which branch based on a single primary key - EmployeeId. 大约有20个表基于单个主键-EmployeeId进行分支。 There's about 12,000 employees I want completely gone from my database. 我想从数据库中完全淘汰掉大约12,000名员工。 The chances of other processes updating these employees when I am deleting them are close to zero. 当我删除员工时,其他进程更新这些员工的机会几乎为零。 I am planning on deleting them in bulk and then committing. 我打算批量删除它们,然后提交。 All the delete shouldn't ideally fail but I am unsure whether to go the cursor route, commit every 500 rows or something. 从理论上讲,所有删除操作都不应该失败,但是我不确定是否要走光标路线,每500行提交一次还是什么。 Here's how it looks like now. 这是现在的样子。

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

If you're going to do this often, how about letting Oracle do the job for you ? 如果您经常这样做,那么让Oracle为您完成这项工作又如何呢?

  1. Set all your foreign keys referencing table Employee to "ON DELETE CASCADE" (see this link for example ) 将所有引用表Employee的外键设置为“ ON DELETE CASCADE”( 例如,请参阅此链接
  2. delete from Employee where <all your conditions>;

The FKs being set to "ON DELETE CASCADE", Oracle will automatically delete orphaned rows from child tables when a row is deleted in the parent table. FK设置为​​“ ON DELETE CASCADE”,当在父表中删除行时,Oracle将自动从子表中删除孤立行。

If you are aware of any trouble during deletion and still want to do entire operation without cursor you could use DML Error Logging : 如果您知道删除过程中遇到的任何麻烦,并且仍然想在没有光标的情况下执行整个操作,则可以使用DML错误记录

In some situations the most obvious solution to a problem is a DML statement (INSERT ... SELECT, UPDATE, DELETE), but you may choose to avoid DML because of the way it reacts to exceptions. 在某些情况下,最明显的解决方案是DML语句(INSERT ... SELECT,UPDATE,DELETE),但是由于DML对异常的反应方式,您可以选择避免DML。

By default, when a DML statement fails the whole statement is rolled back, regardless of how many rows were processed successfully before the error was detected. 默认情况下,当DML语句失败时,将回退整个语句,而不管检测到错误之前成功处理了多少行。

In the past, the only way around this problem was to process each row individually, preferably with a bulk operation using FORALL and the SAVE EXCEPTIONS clause. 过去,解决此问题的唯一方法是单独处理每一行,最好使用FORALL和SAVE EXCEPTIONS子句进行批量操作。 In Oracle 10g Database Release 2, the DML error logging feature has been introduced to solve this problem. 在Oracle 10g数据库第2版中,引入了DML错误日志记录功能来解决此问题。 Adding the appropriate LOG ERRORS clause on to most INSERT, UPDATE, MERGE and DELETE statements enables the operations to complete, regardless of errors. 在大多数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;

Assuming you want to maintain the integrity of the data and when there is an error deleting from one table then you want to ROLLBACK all the deletes for that employee then you could do something like: 假设您要维护数据的完整性,并且从一个表中删除时出错,那么您想要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;

It is not going to be the fastest with a COMMIT at the end of each loop but it does ensure you can ROLLBACK each employee. 在每个循环结束时使用COMMIT并不是最快的方法,但可以确保您可以ROLLBACK每个员工。

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

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