【发布时间】:2014-06-24 05:24:15
【问题描述】:
您能帮我解决以下问题吗:
我正在使用 Oracle Database 11g Enterprise Edition Release 11.1.0.7.0 - 64bit Production,我需要删除一个表。
由于性能原因,我尝试使用 forall 将其删除,但这无法正常工作。假设我有下表:
create table test
(id number (4) not null,
parent_id number(4),
constraint id_pk primary key (id),
constraint fk_id
foreign key (parent_id)
references test(id)
);
有以下记录:
ID PARENT_ID
---------- ----------
1
2 3
3
4 5
5
6 7
7
8
对于删除,我使用以下程序:
declare
l_idx number;
l_err_cnt number;
type tp_tab_num is table of number
index by binary_integer;
a_pid tp_tab_num;
cursor c_pid
is
select id
from test
where id <> 4
start with parent_id is null
connect by prior id = parent_id
order by level desc, id;
begin
open c_pid;
fetch c_pid
bulk collect into a_pid;
begin
forall i in a_pid.first .. a_pid.last save exceptions
delete from test
where id = a_pid (i);
exception
when others then
l_err_cnt := sql%bulk_exceptions.count;
end;
close c_pid;
for i in 1 .. l_err_cnt loop
l_idx := sql%bulk_exceptions (i).error_index;
dbms_output.put_line ( 'Index:' || sql%bulk_exceptions (i).error_index
|| ' Code: ' || sql%bulk_exceptions (i).error_code
|| ' Message: ' || sqlerrm (-sql%bulk_exceptions (i).error_code)
|| ' Pid: ' || a_pid (l_idx)
);
end loop;
end;
forall 的问题在于,如果未从表中删除子记录(例如,由于对其他表的外键约束 - 在我们的案例中,ID 为 4 的记录已从删除游标中排除)
尽管我正在使用保存异常,但 forall 不会删除未删除的子项的父项之后的任何其他记录。
删除过程的输出是:
Index:5 Code: 2292 Message: ORA-02292: integrity constraint (.) violated - child record found Pid: 5 -- Correct: due to constraint fk_id
Index:6 Code: 2292 Message: ORA-02292: integrity constraint (.) violated - child record found Pid: 7 -- Incorrect: child with ID = 6 is already deleted
Index:7 Code: 2292 Message: ORA-02292: integrity constraint (.) violated - child record found Pid: 8 -- Incorrect: ID 8 has no children
请注意,如果我使用 for 循环而不是 forall,它可以正常工作。只有 ID 4 和 5 仍未删除。
谢谢!
【问题讨论】:
-
嗨,我的真实情况是示例中的表测试也有其他表的外键。如果这些没有被删除,我不能从测试中删除记录。因此,如果我使用单个删除它会崩溃。除了保存例外,我只删除我被允许的内容。
-
有问题的外键约束是与表本身的关系。因此,您是否违反约束取决于处理行的顺序。我不知道 FORALL 是否坚持任何给定的顺序。
-
您好,我找不到任何关于 forall 坚持给定顺序的参考资料。但我在桌子上测试过。因此,如果您不排除记录 ID 4 并按级别 desc 保留订单,则删除工作正常。但是,如果您按级别 asc 更改顺序,则脚本将失败并显示 ora 02292。因此,似乎 forall 保持顺序计数。所以我的问题不是来自order by。我仍然认为是一个 oracle 错误。