我是Oracle(11gr2)的新手,我有以下脚本:
BEGIN
DECLARE
source varchar2(1);
BEGIN
dbms_output.enable;
BEGIN
EXECUTE IMMEDIATE 'DROP VIEW SP_AD;';
SELECT SOURCE INTO source FROM map_switch WHERE ROWNUM = 1;
IF source = 'A'
THEN
EXECUTE IMMEDIATE 'DROP TABLE SP_AD_B;';
EXECUTE IMMEDIATE 'RENAME TABLE SP_AD_A TO SP_AD;';
ELSE
EXECUTE IMMEDIATE 'DROP TABLE SP_AD_A;';
EXECUTE IMMEDIATE 'RENAME TABLE SP_AD_B TO SP_AD;';
END IF;
COMMIT WORK;
dbms_output.put_line('SP_AD table issue fixed');
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('Exception, rolling back transaction, SP_AD not resolved.');
ROLLBACK WORK;
END;
END;
END;
/
Run Code Online (Sandbox Code Playgroud)
基本上,它确定要删除哪个表,然后删除视图并重命名另一个表.
如果我单独运行语句,它可以很好地工作,但在上面的脚本中,它返回成功执行的过程但没有执行任何操作.
我怀疑它的回滚有些奇怪的原因,但是我在没有回滚的情况下执行它是犹豫不决的(这些表有超过300,000条记录).
有人可以告诉我什么是错的,并且我的异常块有什么问题吗?
正如评论者所指出的,您的代码无法按预期工作的原因有几个.
首先,不要在传递给的字符串中使用分号EXECUTE IMMEDIATE,因为这样做会给你一个ORA-00911'无效字符'错误:
SQL> BEGIN
2 EXECUTE IMMEDIATE 'DROP TABLE SP_AD_B;';
3 END;
4 /
BEGIN
*
ERROR at line 1:
ORA-00911: invalid character
ORA-06512: at line 2
Run Code Online (Sandbox Code Playgroud)
运行此操作后,您可以验证该表是否仍然存在:
SQL> SELECT * FROM SP_AD_B;
no rows selected
Run Code Online (Sandbox Code Playgroud)
(我没有你的表SP_AD_B,所以我只创建了一个SP_AD_B用一个整数列命名的.我没有把任何数据放进去.)
如果你删除字符串中的分号,而不是外面的分号,它可以工作:
SQL> BEGIN
2 EXECUTE IMMEDIATE 'DROP TABLE SP_AD_B';
3 END;
4 /
PL/SQL procedure successfully completed.
SQL> SELECT * FROM SP_AD_B;
SELECT * FROM SP_AD_B
*
ERROR at line 1:
ORA-00942: table or view does not exist
Run Code Online (Sandbox Code Playgroud)
现在表已经消失了,我们在尝试查询它时遇到错误.
希望这应该允许您修复脚本以使其工作并删除相关表.
但是为什么你没有在输出消息中获得任何有用的信息?好吧,让我们重新创建SP_AD_B表,并重新引入分号,并尝试再次删除表,但使用EXCEPTION类似于你的处理程序:
SQL> BEGIN
2 EXECUTE IMMEDIATE 'DROP TABLE SP_AD_B;';
3 EXCEPTION
4 WHEN OTHERS THEN
5 dbms_output.put_line('Exception, rolling back transaction, SP_AD not resolved.');
6 END;
7 /
Exception, rolling back transaction, SP_AD not resolved.
PL/SQL procedure successfully completed.
Run Code Online (Sandbox Code Playgroud)
在这种情况下,我们收到一条错误消息,告诉我们出错了,所以表格没有丢弃.但出了什么问题?Oracle可以报告数千个错误,如果不知道错误消息,很难猜出问题是什么.
您可以在这里采取多种方法.首先,你可以写错误消息,在SQLERRM给dbms_output:
SQL> BEGIN
2 EXECUTE IMMEDIATE 'DROP TABLE SP_AD_B;';
3 EXCEPTION
4 WHEN OTHERS THEN
5 dbms_output.put_line('Exception, rolling back transaction, SP_AD not resolved.');
6 dbms_output.put_line('Error message was: ' || SQLERRM);
7 END;
8 /
Exception, rolling back transaction, SP_AD not resolved.
Error message was: ORA-00911: invalid character
PL/SQL procedure successfully completed.
Run Code Online (Sandbox Code Playgroud)
dbms_utility.format_error_backtrace如果您愿意,还可以使用将当前堆栈跟踪作为字符串返回.这可能有助于您找出错误的来源.
或者,您可以重新加载例外.RAISE在EXCEPTION处理程序中单独使用会重新引发当前异常:
SQL> BEGIN
2 EXECUTE IMMEDIATE 'DROP TABLE SP_AD_B;';
3 EXCEPTION
4 WHEN OTHERS THEN
5 dbms_output.put_line('Exception, rolling back transaction, SP_AD not resolved.');
6 RAISE;
7 END;
8 /
Exception, rolling back transaction, SP_AD not resolved.
BEGIN
*
ERROR at line 1:
ORA-00911: invalid character
ORA-06512: at line 6
Run Code Online (Sandbox Code Playgroud)
但是,考虑到你的EXCEPTION处理程序并没有真正做任何有用的事情,最好的方法很可能是完全摆脱它.
异常处理程序没有实现任何东西,因为你不能提交或回滚DDL语句,例如CREATE,ALTER,DROP或TRUNCATE.这些语句中的每一个都COMMIT在运行之前和之后立即发出.如果DROP成功但RENAME失败,则无法通过回滚事务来获取已删除的表.我建议你摆脱你的COMMIT WORK和ROLLBACK WORK陈述.
最后,评论者Jeffrey Kemp注意到这一行:
SELECT SOURCE INTO source FROM map_switch WHERE ROWNUM = 1;
Run Code Online (Sandbox Code Playgroud)
这将从表的某个任意行分配一个名为source列值的变量.它可以是任何一行; 因为您没有指定任何排序,Oracle可以自由地订购它喜欢的行.SOURCE map_switchmap_switch
如果表中只有一行,那么您可以清楚地找到哪一行.但是,如果是这种情况,为什么要指定ROWNUM = 1?该表是否有多行,并且该ROWNUM = 1部分是否只是为了使'确切的提取返回超过请求的行数'错误?
你最好做以下事情:
SELECT SOURCE INTO source
FROM (SELECT SOURCE FROM map_switch ORDER BY some_column)
WHERE ROWNUM = 1;
Run Code Online (Sandbox Code Playgroud)
我不知道你的表中有哪些列 map_switch,所以我只是将some_column上面用作其中一个的占位符.如果可能,请选择具有唯一值的列.
请注意,我们不能简单地这样做SELECT ... WHERE ROWNUM = 1 ORDER BY some_column,因为ROWNUM = 1在执行排序之前会应用子句,并且没有很多点对单个行进行排序,因为只有一个可以返回的顺序.