oracle执行立即不执行

Ste*_*afa 0 plsql oracle11g

我是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条记录).

有人可以告诉我什么是错的,并且我的异常块有什么问题吗?

Luk*_*ard 7

正如评论者所指出的,您的代码无法按预期工作的原因有几个.

首先,不要在传递给的字符串中使用分号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可以报告数千个错误,如果不知道错误消息,很难猜出问题是什么.

您可以在这里采取多种方法.首先,你可以写错误消息,在SQLERRMdbms_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如果您愿意,还可以使用将当前堆栈跟踪作为字符串返回.这可能有助于您找出错误的来源.

或者,您可以重新加载例外.RAISEEXCEPTION处理程序中单独使用会重新引发当前异常:

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,DROPTRUNCATE.这些语句中的每一个都COMMIT在运行之前和之后立即发出.如果DROP成功但RENAME失败,则无法通过回滚事务来获取已删除的表.我建议你摆脱你的COMMIT WORKROLLBACK 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在执行排序之前会应用子句,并且没有很多点对单个行进行排序,因为只有一个可以返回的顺序.

  • 谢谢!现在这是一个教科书答案,它应该是 SO 上所有答案的模板。我希望我能多点赞 (2认同)