我是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;
/
基本上,它确定要删除哪个表,然后删除视图并重命名另一个表。
如果我单独运行语句,它可以很好地工作,但在上面的脚本中,它返回成功执行但没有执行的程序。
我怀疑它因为一些奇怪的原因而回滚,但我在没有回滚的情况下犹豫不决(这些表有超过300,000条记录)。
有人可以告诉我什么是错的,而且我的异常块有什么问题吗?
答案 0 :(得分:6)
正如评论者所指出的,有几个原因导致您的代码无法按预期运行。
首先,不要在传递给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
运行此功能后,您可以验证该表是否仍然存在:
SQL> SELECT * FROM SP_AD_B;
no rows selected
(我没有你的表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
现在表格已经消失,我们在尝试查询时遇到错误。
希望这应该允许您修复脚本以使其正常工作并删除相关表。
但是为什么你的输出信息中没有得到任何有用的信息?好吧,让我们重新创建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.
在这种情况下,我们收到一条错误消息,告诉我们出错了,所以表格没有丢弃。但 出了什么问题? 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.
如果您愿意,也可以使用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
但是,鉴于你的EXCEPTION
处理程序并没有真正做任何有用的事情,最好的方法很可能完全摆脱它。
您的异常处理程序无法实现任何功能,因为您无法提交或回滚CREATE
,ALTER
,DROP
或TRUNCATE
等DDL语句。这些语句中的每一个都在运行之前和之后立即发出COMMIT
。如果DROP
成功但RENAME
失败,则无法通过回滚事务来获取已删除的表。我建议您删除COMMIT WORK
和ROLLBACK WORK
语句。
最后,评论者Jeffrey Kemp注意到这一行:
SELECT SOURCE INTO source FROM map_switch WHERE ROWNUM = 1;
这将从表source
的某些任意行中将SOURCE
列的值分配给名为map_switch
的变量。它可以是任何一行;因为您没有指定任何排序,Oracle可以自由地订购map_switch
行,但它喜欢。
如果表格中只有一行,那么您可以清楚地找到哪一行。但是,如果是这种情况,为什么要指定ROWNUM = 1
?该表是否有多行,并且ROWNUM = 1
部分是否可以使“确切的提取返回超过请求的行数”错误?
你最好做以下事情:
SELECT SOURCE INTO source
FROM (SELECT SOURCE FROM map_switch ORDER BY some_column)
WHERE ROWNUM = 1;
我不知道您的map_switch
表格中有哪些列,所以我刚刚使用上面的some_column
作为其中一个的占位符。如果可能,请选择具有唯一值的列。
请注意,我们不能简单地执行SELECT ... WHERE ROWNUM = 1 ORDER BY some_column
因为在执行排序之前应用ROWNUM = 1
子句,并且没有很多点对单行进行排序,因为它只能有一个顺序被退回。