0

私たちのシステムの 1 つで SP の保守性を改善しようとしているときに、値の配列 (この場合はテーブル名) をハードコードするよりもループを使用する方がよいと判断し、それに応じてコードをリファクタリングしようとしました。またはシステムからテーブルを削除しても、配列を編集する必要はありませんでした。今のところ、ループの理由と理由を脇に置いて (私はそれらに対する議論をよく知っています)、誰かが何が起こっているのか説明できますか?

同じデータベースに SourceUser と DestUser という 2 人のユーザーがいて、それぞれのテーブルが同じテーブルスペースにあるとします。SourceUser の一連のストアド プロシージャは、レポート目的で SourceUser から DestUser にデータを取り込みます。この一環として、実行される最初のプロシージャは、DestUser 内のすべてのテーブルを削除し、それらを再作成します。繰り返しますが、ここでそれを行うことの相対的なメリットについては議論しません。

SourceUser には、DestUser に対する Drop Any Table 権限と Create Any Table 権限があります。DestUser には保持したいテーブルが 1 つあります。したがって、手順で作成した SQL は次のようになります。

Begin
  For T In (SELECT TABLE_NAME FROM all_tables WHERE TABLE_NAME != 'MIDBLOG' AND OWNER = sTarget_DB) Loop
    Begin
      Execute Immediate('Drop Table ' || sTarget_DB || '.' || T.TABLE_NAME);

    Exception
      When Others Then
        --Don't care if we get an exception here as most likely the table wasn't there to be dropped in the first place.
        NULL;
    End;
  End Loop;
End;

この場合、sTarget_DB は DestUser に設定され、このコードは SourceUser に対して実行されています。

手順を実行すると、テーブルが削除されていないことがわかります (開始前に、MIDBLOG という名前のテーブルを含む数十のテーブルがあることを確認しました)。私はそれをSQL Developerデバッグモードで実行しましたが、処理する行がないと思われるため、実行はループの内側に到達することさえありませんが、selectステートメントが数十のテーブル名を返すことは確かです.

次に、これを次のように修正しました。

Begin
  For T In (SELECT TABLE_NAME FROM all_tables WHERE OWNER = sTarget_DB) Loop
    Begin
      If T.TABLE_NAME != 'MIDBLOG' THEN
        Execute Immediate('Drop Table ' || sTarget_DB || '.' || T.TABLE_NAME);
      End If;
    Exception
      When Others Then
        --Don't care if we get an exception here as most likely the table wasn't there to be dropped in the first place.
        NULL;
    End;
  End Loop;
End;

これを実行した後、削除された唯一のテーブルは、私が削除したくないものでした! さらに奇妙なのは、select クエリが 1 行だけを返すかのように、ループが 1 回しか実行されなかったことです。SQL Developer 3.2 でデバッグ中にプロシージャを実行すると、この問題が発生することがわかりました。SQL Developer (おそらく 3.1) 上の同僚の PC で同じことを行いましたが、ループは 1 回だけ実行されましたが、今回はテーブル MIDBLOG を削除しないことを正しく決定し、他のすべてをそのままにしました。

上記の例のいずれかを SQL Developer で無名ブロックとして実行すると、期待どおりの動作をします。より冗長な明示カーソル宣言を試してみたところ、以前と同じ結果になりました。例外は一度もありませんでした。

これはすべて、Oracle 10g Enterprise Edition リリース 10.2.0.4.0 (64 ビット) にありました。Oracle 11g Enterprise Edition Release 11.2.0.1.0 (64bit) で試してみると、問題なく動作しました。いったいなぜ、このような基本的な要件が、2 つのバージョン間でこれほど大きく異なる動作を示すのでしょうか? 同じビットのコードを使用して、両方のバージョンで思い通りに動作することはできますか?

4

1 に答える 1

2

私の推測では、問題は Oracle のバージョンではなく権限に関連していると思われます。権限は、一方のデータベースではロールを介して DestUser付与され、もう一方のデータベースでは直接付与を介して付与されますか?SrcUser

匿名 PL/SQL ブロックを実行する前に、最初にロールを無効にするとどうなりますか?

set role none;
<<run the anonymous PL/SQL block>>

コードにインストルメンテーションを追加した場合、クエリはall_tables期待どおりのテーブルのセットを返しますか? 私の推測では、コードが失敗すると、プロシージャの所有者がDestUserロールを介してテーブルにアクセスできる定義者権限ストアド プロシージャにあると思います。ロールを介して付与された権限は、定義者権限のストアド プロシージャには表示されないため、SELECTループ内のステートメントは 0 行を返します (ただし、同じクエリを対話的に実行すると、期待どおりの行が返されます)。一方、テーブルに対する権限DestUserが直接付与されている場合は、同じ定義者権限のストアド プロシージャが正常に実行されます。そして、匿名の PL/SQL ブロックで機能します。

于 2012-11-29T19:12:46.953 に答える