【问题标题】:oracle execute immediate not executingoracle 立即执行 不执行
【发布时间】:2013-11-06 19:11:14
【问题描述】:

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

谁能告诉我出了什么问题,还有,我的异常块有问题吗?

【问题讨论】:

  • 从动态查询中删除终止分号!
  • 您不能回滚 DDL 语句。他们也不需要 COMMIT。
  • 您对 EXCEPTION 块做错的一件事是您捕获所有异常 (WHEN OTHERS) 但您没有将消息写入 dbms_output 或重新提出它。在EXCEPTION WHEN OTHERS THEN 块中添加一行如dbms_output.put_line('Error was ' || SQLERRM); 会告诉你问题出在哪里。
  • @LukeWoodward 看起来怎么样?
  • 为什么只检索map_switch 的第一条记录?这是一种代码味道,因为它是不确定的。

标签: plsql oracle11g


【解决方案1】:

正如评论者所指出的,您的代码无法按预期运行有几个原因。

首先,不要在传递给EXECUTE IMMEDIATE 的字符串中使用分号,否则会出现 ORA-00911 'invalid character' 错误:

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 可以报告数以千计的错误,在不知道错误消息的情况下很难猜出问题所在。

您可以在这里采取多种方法。首先,您可以将错误消息写入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.

如果您愿意,也可以使用dbms_utility.format_error_backtrace 将当前堆栈跟踪作为字符串返回。这可能会帮助您找出错误的来源。

或者,您可以重新引发异常。在 EXCEPTION 处理程序中单独使用 RAISE 会重新引发当前异常:

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 处理程序实际上并没有做任何有用的事情,最好的方法很可能是完全摆脱它。

您的异常处理程序没有实现任何目标,因为您无法提交或回滚 DDL 语句,例如 CREATEALTERDROPTRUNCATE。这些语句中的每一个在它运行之前和之后都会立即发出一个COMMIT。如果DROP 成功但RENAME 失败,则无法通过回滚事务来恢复已删除的表。我建议摆脱您的 COMMIT WORKROLLBACK 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 子句,并且对单行进行排序没有太多意义,因为它只能返回一个顺序。

【讨论】:

  • 谢谢!现在这是一个教科书答案,应该是所有答案的模板。我希望我能更多地支持这一点
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-11
  • 2018-09-27
  • 1970-01-01
  • 2019-10-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多