我正在尝试编写一个死锁证明存储过程。它基于以下概念。
我一直在尝试编写一个基于以下概念的存储过程。该过程将尝试在表上删除约束,如果它检测到死锁情况,则等待一段时间再重试。重要的是它只应该在死锁或NOWAIT错误的情况下重试,所有其他错误都要通过异常处理。
Procedure test
is
BEGIN
<<label>>
DROP constraint on a table
if (deadlock(ORA-00060)/Nowait Error (ORA-0054)) detected
then
sleep for 60 seconds
Goto label
exception
when others.
如果有任何专家请帮我解决这个问题会很棒。类似的例子非常有帮助。谢谢你的帮助。
答案 0 :(得分:3)
虽然有些人对goto
抱有非理性的厌恶,但通常我们可以在不使用构造的情况下实现相同的逻辑。这是真的:只需要一个简单的while
循环:
create or replace procedure drop_constraint_for_sure
( p_table_name in varchar2
, p_constraint_name in varchar2
)
is
x_deadlock exception;
pragma exception_init(x_deadlock, -60);
x_nowait exception;
pragma exception_init(x_nowait, -54);
begin
loop
begin
execute immediate 'alter table '|| p_table_name
|| ' drop constraint ' || p_constraint_name
|| ' cascade' ;
exit;
exception
when x_deadlock then null;
when x_nowait then null;
end;
dbms_lock.sleep(60);
end loop;
end;
/
请注意,sleep函数需要SYS.DBMS_LOCK的execute
权限。这是默认情况下未授予,因此如果您没有,则需要请求您的友好DBA授予它。
另请注意,此实现不具有任何形式的中止。因此它将永远循环,直到约束被删除或发生其他一些异常。在现实生活中,您应该包括一个循环计数,并在计数阈值上进行额外的退出测试。虽然在现实生活中我可能不会想要这样的存储过程:我更喜欢尽快知道有人在使用桌子时我试图改变。
答案 1 :(得分:1)
希望下面的代码段可以让您了解实现您的要求。
CREATE OR REPLACE
PROCEDURE DRP_CONST_DEALDOCK
AS
DEADLOCK_EX EXCEPTION;
NO_WAIT_EX EXCEPTION;
PRAGMA EXCEPTION_INIT(DEADLOCK_EX,-60);
PRAGMA EXCEPTION_INIT(NO_WAIT_EX, -54);
lv_cnt PLS_INTEGER;
BEGIN
BEGIN
EXECUTE IMMEDIATE 'ALTER TABLE STACK_OF_TEST DROP CONSTRAINT SYS_C00375020';
EXCEPTION
WHEN DEADLOCK_EX THEN
dbms_output.put_line('nowait exception');
DBMS_LOCK.SLEEP(10);
SELECT COUNT(1)
INTO lv_cnt
FROM v$locked_object
WHERE object_id IN
(SELECT OBJECT_ID FROM DBA_OBJECTS WHERE OBJECT_NAME = 'STACK_OF_TEST'
);
WHILE lv_cnt > 0
LOOP
dbms_lock.sleep(10);
SELECT COUNT(1)
INTO lv_cnt
FROM v$locked_object
WHERE object_id IN
(SELECT OBJECT_ID FROM DBA_OBJECTS WHERE OBJECT_NAME = 'STACK_OF_TEST'
);
END LOOP;
WHEN NO_WAIT_EX THEN
dbms_output.put_line('nowait exception');
DBMS_LOCK.SLEEP(10);
SELECT COUNT(1)
INTO lv_cnt
FROM v$locked_object
WHERE object_id IN
(SELECT OBJECT_ID FROM DBA_OBJECTS WHERE OBJECT_NAME = 'STACK_OF_TEST'
);
WHILE lv_cnt > 0
LOOP
dbms_lock.sleep(10);
SELECT COUNT(1)
INTO lv_cnt
FROM v$locked_object
WHERE object_id IN
(SELECT OBJECT_ID FROM DBA_OBJECTS WHERE OBJECT_NAME = 'STACK_OF_TEST'
);
END LOOP;
WHEN OTHERS THEN
dbms_output.put_line(SQLCODE||'-->'||SQLERRM);
END;
END;