web-dev-qa-db-ja.com

一意のインデックスが存在する場合、Oracle10gと11gの間でREFCURSORの動作が異なりますか?

説明

開発インスタンスと、Oracle 8、9、10、最近11を実行する複数のクライアントテストインスタンスと本番インスタンスの両方でローカルに7年ほど実行されているOracleストアドプロシージャがあります。 Oracle11g。基本的に、このプロシージャは参照カーソルを開き、テーブルを更新してから完了します。 10gではカーソルに期待される結果が含まれますが、11gではカーソルは空になります。 11gへのアップグレード後、DMLまたはDDLは変更されていません。この動作は、私が試したすべての10gまたは11gインスタンスで一貫しています(10.2.0.3、10.2.0.4、11.1.0.7、11.2.0.1-すべてWindowsで実行されています)。

特定のコードははるかに複雑ですが、やや現実的な概要で問題を説明します。ヘッダーテーブルとPDFに出力される子テーブルの束にいくつかのデータがあります。ヘッダーテーブルには、そのデータがまだ処理されているかどうかを示すブール値(NUMBER(1)、0はfalse、1はtrue)列があります。

ビューは、処理されていない行のみを表示するように制限されています(ビューは他のいくつかのテーブルでも結合し、インラインクエリや関数呼び出しを行います)。したがって、カーソルが開かれると、ビューには1つ以上の行が表示され、カーソルが開かれた後、更新ステートメントが実行されてヘッダーテーブルのフラグが反転され、コミットが発行されて、プロシージャが完了します。

10gでは、カーソルが開き、行が含まれます。次に、updateステートメントがフラグを反転し、プロシージャを2回実行しても、データは生成されません。

11gでは、カーソル 決して 行が含まれている場合、updateステートメントが実行されるまでカーソルが開かないように見えます。

11g(できれば設定可能な設定)で何かが変更され、他の手順や他のアプリケーションに影響を与える可能性があるのではないかと心配しています。私が知りたいのは、2つのデータベースバージョン間で動作が異なる理由を誰かが知っているかどうか、そしてコードを変更せずに問題を解決できるかどうかです。

アップデート1: 問題を一意の制約まで追跡することができました。 11gに一意性制約が存在する場合、実際のオブジェクトに対して実際のコードを実行しているか、次の簡単な例を実行しているかに関係なく、問題は100%再現可能であるようです。

アップデート2: 方程式からビューを完全に排除することができました。簡単な例を更新して、テーブルに対して直接クエリを実行した場合でも問題が存在することを示しました。

簡単な例

CREATE TABLE tbl1
(
  col1  VARCHAR2(10),
  col2  NUMBER(1)
);

INSERT INTO tbl1 (col1, col2) VALUES ('TEST1', 0);

/* View is no longer required to demonstrate the problem
CREATE OR REPLACE VIEW vw1 (col1, col2) 
AS 
SELECT col1, col2 
  FROM tbl1 
 WHERE col2 = 0;
*/

CREATE OR REPLACE PACKAGE pkg1
AS
   TYPE refWEB_CURSOR IS REF CURSOR;

   PROCEDURE proc1 (crs  OUT  refWEB_CURSOR);

END pkg1;

CREATE OR REPLACE PACKAGE BODY pkg1 
IS
   PROCEDURE proc1 (crs  OUT  refWEB_CURSOR)
   IS
   BEGIN

      OPEN crs FOR
        SELECT col1
          FROM tbl1
         WHERE col1 = 'TEST1'
           AND col2 = 0;

      UPDATE tbl1
         SET col2 = 1
       WHERE col1 = 'TEST1';

      COMMIT;

   END proc1;

END pkg1;

匿名ブロックデモ

DECLARE 
   crs1  pkg1.refWEB_CURSOR;

   TYPE rectype1 IS RECORD (
      col1  vw1.col1%TYPE
   );

   rec1  rectype1;
BEGIN 
   pkg1.proc1 ( crs1 );

   DBMS_OUTPUT.PUT_LINE('begin first test');

   LOOP
      FETCH crs1
       INTO rec1;

      EXIT WHEN crs1%NOTFOUND;

      DBMS_OUTPUT.PUT_LINE(rec1.col1);

   END LOOP;  

   DBMS_OUTPUT.PUT_LINE('end first test');

END; 

/* After creating this index, the problem is seen */
CREATE UNIQUE INDEX unique_col1 ON tbl1 (col1);

/* Reset data to initial values */
TRUNCATE TABLE tbl1;

INSERT INTO tbl1 (col1, col2) VALUES ('TEST1', 0);

DECLARE 
   crs1  pkg1.refWEB_CURSOR;

   TYPE rectype1 IS RECORD (
      col1  vw1.col1%TYPE
   );

   rec1  rectype1;
BEGIN 
   pkg1.proc1 ( crs1 );

   DBMS_OUTPUT.PUT_LINE('begin second test');

   LOOP
      FETCH crs1
       INTO rec1;

      EXIT WHEN crs1%NOTFOUND;

      DBMS_OUTPUT.PUT_LINE(rec1.col1);

   END LOOP;  

   DBMS_OUTPUT.PUT_LINE('end second test');

END; 

10gでの出力の例:
最初のテストを開始します
TEST1
最初のテストを終了する
2番目のテストを開始します
TEST1
2番目のテストを終了します

11gでの出力の例:
最初のテストを開始します
TEST1
最初のテストを終了する
2番目のテストを開始します
2番目のテストを終了します

明確化

実際のシナリオでは、プロシージャがWebアプリケーションから呼び出されるため、COMMITを削除できません。フロントエンドのデータプロバイダーがプロシージャを呼び出すと、とにかくデータベースから切断するときに暗黙のCOMMITが発行されます。したがって、手順でCOMMITを削除すると、はい、匿名ブロックのデモは機能しますが、実際のシナリオでは、COMMITが引き続き発生するため機能しません。

質問

11gの動作が異なるのはなぜですか?コードを書き直す以外にできることはありますか?

18
wweicker

これはごく最近発見されたバグのようです。 Metalink Bug 1045196 は正確な問題を説明しています。パッチがまもなくリリースされることを願っています。メタリンクの壁を乗り越えることができない人のために、ここにいくつかの詳細があります:

メタリンク

バグ10425196:PL/SQLが11.1.0.6と10.2.0.5でREFCURSORの動作が異なる

タイプ:欠陥
重大度:2-重大なサービスの喪失
ステータス:作成されたコードバグ:2010年12月22日

元のケース提出からの診断分析
-10.2.0.4Windowsの期待される動作
-10.2.0.5Solarisの期待される動作
-11.1.0.6Solarisの予期しない動作
-11.1.0.7Windowsの予期しない動作
-11.2.0.1Solarisの予期しない動作
-11.2.0.2Solarisの予期しない動作

詳細は確認できます
-10.2.0.3Windowsの期待される動作
-11.2.0.1Windowsの予期しない動作

さらなる詳細

を変更する OPTIMIZER_FEATURES_ENABLE = '10 .2.0.4 ' パラメータは問題を解決しません。したがって、オプティマイザの調整ではなく、11gデータベースエンジンの設計変更に関連しているようです。

コードの回避策

これは、テーブルをクエリするときにインデックスを使用した結果であり、テーブルを更新したりコミットしたりする行為ではないようです。上記の例を使用して、クエリがインデックスを使用しないようにする2つの方法を次に示します。どちらもクエリのパフォーマンスに影響を与える可能性があります。

クエリのパフォーマンスへの影響は、パッチがリリースされるまで一時的に許容される場合がありますが、@ Edgar Chupitが提案したようにFLASHBACKを使用すると、インスタンス全体のパフォーマンスに影響を与える可能性があるため(または、一部のインスタンスでは使用できない場合があります)、オプションが適用されない場合があります。一部の人には受け入れられます。いずれにせよ、現時点では、コードの変更が唯一の既知の回避策であるように思われます。

方法1:コードを変更して、この1つの列の一意のインデックスが使用されないように、関数で列をラップします。私の場合、列は一意ですが小文字が含まれることはないため、これは許容されます。

    SELECT col1
      FROM tbl1
     WHERE UPPER(col1) = 'TEST1'
       AND col2 = 0;

方法2:インデックスが使用されないようにするヒントを使用するようにクエリを変更します。あなたは期待するかもしれません NO_INDEX(unique_col1) 動作するためのヒントですが、動作しません。ザ・ ルール ヒントは機能しません。あなたは使用することができます FULL(tbl1) ヒントですが、これはメソッド1を使用するよりもクエリの速度を低下させる可能性があります。

    SELECT /*+ FULL(tbl1) */ col1
      FROM tbl1
     WHERE col1 = 'TEST1'
       AND col2 = 0;


オラクルの対応と提案された回避策

オラクルのサポートは、最終的に次のMetalinkアップデートで対応しました。

Oracleサポート-2011年7月20日5:51:19 AM GMT-07:00 [ODM提案ソリューション]
開発では、これは修正すべき重要な問題であると報告されており、
は次の回避策を適用することを提案しています:
 
 init.ora/spfileを次のように編集します文書化されていないパラメータ:
 
 "_ row_cr" = false 
 
Oracleサポート-2011年7月20日5:49:20 AM GMT-07:00 [ODM原因の正当化]
開発により、これは欠陥であると判断されました
 
Oracleサポート-2011年7月20日5:48:27 AM GMT-07:00 [ODM原因判別]
原因は行ソースカーソルの最適化にトレースされています
 
Oracleサポート-2011年7月20日5:47:27 AM GMT-07:00 [ODM問題の検証]
開発により、これが11.2.0.1 
の問題であることが確認されました。

さらに対応した後、これはバグとして扱われているのではなく、設計上の決定が進んでいるように聞こえます。

Oracleサポート-2011年7月21日5:58:07 AM GMT-07:00 [ODM提案ソリューションJustif]
 10.2.0.5以降(11.2.0.2を含む)には、
 ROWCRという最適化があります。これは一意のインデックスを使用して
の行を決定するクエリにのみ適用できます。表。
 
この最適化の簡単な概要は、現在のブロックにコミットされていない変更がない場合、
 CRブロックの構築中にロールバックを回避しようとすることです。
 
したがって、11.2.0.2で見られる違いは、この最適化によるものです。 
推奨される回避策は、この最適化をオフにして、
 10.2.0.4で使用されていたものとまったく同じように機能するようにすることです[.____]。

私たちの場合、クライアント環境を考えると、単一のストアドプロシージャに分離されているため、引き続き使用します。 コードの回避策 インスタンス全体の未知の副作用が他のアプリケーションやユーザーに影響を与えるのを防ぐため。

7
wweicker

これは確かに奇妙な問題です、共有してくれてありがとう!

Oracle 11.1以降のOracleでの動作の変更のように見え、メタリンクで同様の問題が発生することが確認されています(bug#10425196)。残念ながら、現時点では、主題に関するメタリンクに関する情報はあまりありませんが、OracleでSRを開いて、さらに情報を提供するように依頼しました。

現時点では、その理由を説明することはできませんが、この動作を10gスタイルに戻すことができる(非表示の)パラメーターがある場合は、回避策を提供できると思います。 Oracleフラッシュバッククエリ機能を使用して、Oracleに予想される時点のデータを取得させることができます。

次のようにコードを変更した場合:

OPEN crs FOR 
  SELECT col1
>>> FROM vw1 as of scn dbms_flashback.get_system_change_number
   WHERE col1 = 'TEST1';

その場合、結果は10gと同じになります。

そして、これは元のテストケースの簡略版です。

SQL> drop table tbl1;
Table dropped
SQL> create table tbl1(col1 varchar2(10), col2 number);
Table created
SQL> create unique index tbl1_idx on tbl1(col1);
Index created
SQL> insert into tbl1(col1,col2) values('TEST1',0);
1 row inserted
SQL> DECLARE
  2    cursor web_cursor is
  3          SELECT col1
  4            FROM tbl1
  5           WHERE col2 = 0 and col1 = 'TEST1';
  6  
  7    rec1  web_cursor%rowtype;
  8  BEGIN
  9    OPEN web_cursor;
 10  
 11    UPDATE tbl1
 12       SET col2 = 1
 13     WHERE col1 = 'TEST1';
 14  
 15    -- different result depending on commit!
 16    commit;
 17  
 18     DBMS_OUTPUT.PUT_LINE('Start');
 19     LOOP
 20        FETCH web_cursor
 21         INTO rec1;
 22  
 23        EXIT WHEN web_cursor%NOTFOUND;
 24  
 25        DBMS_OUTPUT.PUT_LINE(rec1.col1);
 26     END LOOP;
 27     DBMS_OUTPUT.PUT_LINE('Finish');
 28  END;
 29  /

Start
Finish

PL/SQL procedure successfully completed

16行目でcommitをコメントアウトすると、出力は次のようになります。

Start
TEST1
Finish

PL/SQL procedure successfully completed
2
Edgar Chupit

Metalink(別名Oracleサポート)から

ステータスバグ10425196:92-バグではなくクローズ

問題:

REF CURSORを返すストアドプロシージャを呼び出すと、10.2.0.5以前のデータベースと11.1.0.6以降のデータベースで異なる動作が見られます。

イベントのシーケンス

  1. 参照カーソルを渡すストアドプロシージャを呼び出す
  2. TableAに対して参照カーソルを開く
  3. ストアドプロシージャ内からTableA内の一部のデータを更新します
  4. 更新をコミットする
  5. プロシージャの実行が終了し、参照カーソルが呼び出し元に返されます

10.2.0.5以前

返されたカーソルは、データが更新される前に開かれたため、更新されたデータを認識しません。これは予想される動作です。

11.1.0.6以降

返されたカーソルは更新されたデータを確認し、10.2.0.5以前の動作とは異なる更新されたデータを返します。

診断分析:

10.2.0.4Windowsの予期される動作10.2.0.5Solarisの予期される動作11.1.0.6Solarisの予期しない動作11.1.0.7Windowsの予期しない動作11.2.0.1Solarisの予期しない動作11.2.0.2Solarisの予期しない動作

関連するバグ:

見つかりませんでした。

必要に応じて、次の起動パラメータを設定して10.2.0.5より前の動作に戻し、データベースを再起動できます。

_row_cr = false

1
junaling