0

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

有人可以告诉我出了什么问题,而且我的异常块有问题吗?

4

1 回答 1

9

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

首先,不要在传递给的字符串中使用分号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 可以报告数以千计的错误,在不知道错误消息的情况下很难猜出问题所在。

您可以在此处采用多种方法。首先,您可以将错误消息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处理程序实际上并没有做任何有用的事情,最好的方法很可能是完全摆脱它。

您的异常处理程序没有实现任何目标,因为您无法提交或回滚 DDL 语句,例如CREATE、或. 这些语句中的每一个都在运行之前和之后立即发出一个。如果 a成功但 a失败,则无法通过回滚事务来取回已删除的表。我建议摆脱你的and陈述。ALTERDROPTRUNCATECOMMITDROPRENAMECOMMIT WORKROLLBACK WORK

最后,评论者 Jeffrey Kemp 注意到了这一行:

SELECT SOURCE INTO source FROM map_switch WHERE ROWNUM = 1;

这会将表中任意行的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;

我不知道您的 map_switch表格中有哪些列,所以我只是在some_column上面用作其中之一的占位符。如果可能,请选择具有唯一值的列。

请注意,我们不能简单地这样做SELECT ... WHERE ROWNUM = 1 ORDER BY some_column,因为这会在进行排序之前应用该ROWNUM = 1子句,并且对单行进行排序的点并不多,因为它只能返回一个顺序。

于 2013-11-07T19:38:48.223 回答