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 '無効な文字' エラーが発生します。

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のDDL ステートメントをコミットまたはロールバックできないため、例外ハンドラーは何も達成しません。これらの各ステートメントは、実行の直前と直後に を発行します。aが成功しても aが失敗した場合、トランザクションをロールバックしてもドロップされたテーブルを元に戻すことはできません。andステートメントを削除することをお勧めします。DROPTRUNCATECOMMITDROPRENAMECOMMIT WORKROLLBACK WORK

最後に、コメンターの Jeffrey Kemp が次の行に気付きました。

SELECT SOURCE INTO source FROM map_switch WHERE ROWNUM = 1;

これは、テーブルの任意の行からsourceの列の値という名前の変数に割り当てます。それは任意の行である可能性があります。順序を指定していないため、Oracle は自由に行を並べ替えることができます。SOURCE map_switchmap_switch

テーブルに行が 1 つしかない場合は、どの行が返されるかは明らかです。しかし、これが事実である場合、なぜ指定するの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。単一の行をソートしても、返される順序は 1 つしかないため、あまり意味がありません。

于 2013-11-07T19:38:48.223 に答える