请你帮我解决以下问题:
我使用的是Oracle Database 11g企业版11.1.0.7.0版 - 64位生产版,我需要删除一个表。
由于性能原因,我试图使用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仍未删除。
谢谢!
答案 0 :(得分:0)
将约束设置为DEFERRED
时,它应该有效。然后它仅适用于COMMIT
。
或使用单个DELETE语句:
delete from test
where id =any
(select id
from test
where id <> 4
start with parent_id is null
connect by prior id = parent_id);
修改强>
反过来说:
DELETE FROM TEST
WHERE ID <>ALL
(SELECT ID
FROM TEST
START WITH ID = 4
CONNECT BY ID = PRIOR parent_id);
一些注意事项:
CONNECT BY
子句,即从底部开始
从顶部<>ALL ()
等于NOT IN ()
START WITH ID IN (4,6)