[英]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为您完成这项工作又如何呢?
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.