3

最近、DB を 9i から 10G に移行しました (はい..遅いほうがいいです。いいえ - 11g への移行は現在オプションではありません :-))

私のOracle 10G DBの詳細は次のとおりです:-

Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod
PL/SQL Release 10.2.0.1.0 - Production
CORE    10.2.0.1.0      Production

その動き以来、私は非常に奇妙な問題に直面しています。9i で問題なく動作していたクエリが、10G では動作しません。

rownum に関連する他の SO の質問を検索しましたが、似たようなものを実際に見つけることができませんでした。

SQLクエリは:-

SELECT * FROM 
( SELECT field1, field2 , field3, field4, field5, field6, field7, to_char(rownum) field8
  FROM
    ( SELECT 
        field1,
        field2,
        field3,
        field4,
        field5,
        field6,
        field7,
        ''
      FROM
      .......REST OF MY COMPLEX INNER QUERY
  )
) 
WHERE field8 BETWEEN 21 AND 30;

基本的に、21 / 30 は、ページネーションのためにクエリに渡されるレコードのインデックスである数値であり、9i では、このクエリは期待どおりに機能し、指定されたデータ セットのみを返します。

ただし、10G では、この同じクエリはまったく機能せず、常に 0 レコードが返されます。

クエリのrownum関連部分にコメントすると:-

to_char(rownum) field8  and
WHERE field8 BETWEEN 21 AND 30;

次に、結果セット全体を取得します。それは素晴らしいことです。しかし、私の意図はrownumを使用してページネーションを行うことであるため、目的全体が無効になります。

このクエリが 10G で機能しなくなった理由を知っている人はいますか? rownum 実装の更新を調べてみましたが、実際に役立つものは何も見つかりませんでした。

編集:- デバッグを行っているときに、意味をなさないものに遭遇しました。それなしでは説明できないので、以下にクエリ全体を入れています。

SELECT * FROM 
( SELECT field1, field2 , field3, field4, field5, field6, field7, to_char(rownum) field8 from 
 ( SELECT PM.POLICY_NO field1
   ,PM.INSURED_CODE field2
   ,PM.INSURED_NAME field3
   ,TO_CHAR(PM.POLICY_EFFECTIVE_DATE,'DD/MM/YYYY') field4
   ,TO_CHAR(PM.POLICY_EXPIRATION_DATE,'DD/MM/YYYY') field5
   ,'' field6
   ,'' field7
   ,'' field8
   FROM POLICY_MAIN PM
   ,POLICY_ENDORSEMENT_MAIN PEM
   ,MASTER_UW_LOB_CLASS MAS
   WHERE PM.POLICY_NO = PEM.POLICY_NO
   AND PM.POLICY_NO LIKE UPPER('%%')
   AND PM.INSURED_CODE LIKE UPPER('%%')
   AND PM.SOURCE_OF_BUSINESS LIKE UPPER('%%')
   AND PM.POLICY_TYPE IS NULL
   AND PM.POLICY_STATUS = 'POST'
   AND PM.POLICY_LOB = MAS.UW_LOB_CODE
   AND MAS.UW_CLASS_CODE LIKE UPPER('AUTO')
   AND PEM.POLICY_ENDORSEMENT_NO =
    (SELECT MAX(PEM2.POLICY_ENDORSEMENT_NO)
     FROM   POLICY_ENDORSEMENT_MAIN       PEM2
     WHERE  PEM.POLICY_NO                 = PEM2.POLICY_NO
     ***AND    PEM.ENDORSEMENT_STATUS        = 'POST'***
     )
   ***order by 1 ASC***
  )
) 
WHERE field8 BETWEEN 21 AND 40

最も内側のサブクエリの *** で囲まれた行を参照してください。

  1. クエリからこの行にコメントを付けると、クエリは正常に機能します。

    AND PEM.ENDORSEMENT_STATUS = '投稿'

  2. クエリからこの行にコメントを付けても、他のすべてが元から変更されていない場合、クエリも正常に機能します

    1ASCで注文

rownum に関連する以前のポイントは引き続き当てはまりますが、これらの行を個別にコメントすると、rownum が無関係になり、クエリ全体が正常に機能するようになります (結果が論理的に異なるという事実を除いて)。

私は混乱しています。控えめに言って!!!

編集2:

上記のクエリの実行プランを追加する

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=19 Card=1 Bytes=114)

   1    0   VIEW (Cost=19 Card=1 Bytes=114)
   2    1     COUNT
   3    2       FILTER
   4    3         VIEW (Cost=17 Card=1 Bytes=128)
   5    4           SORT (ORDER BY) (Cost=17 Card=1 Bytes=130)
   6    5             TABLE ACCESS (BY INDEX ROWID) OF 'POLICY_ENDORSEMENT_MAIN' (TABLE) (Cost=2 Card=1 Bytes=39)
   7    6               NESTED LOOPS (Cost=16 Card=1 Bytes=130)
   8    7                 NESTED LOOPS (Cost=14 Card=1 Bytes=91)
   9    8                   TABLE ACCESS (FULL) OF 'POLICY_MAIN' (TABLE) (Cost=14 Card=1 Bytes=82)
  10    8                   INDEX (UNIQUE SCAN) OF 'PK_MASTER_UW_LOB_CLASS' (INDEX (UNIQUE)) (Cost=0 Card=1 Bytes=9)
  11    7                 INDEX (RANGE SCAN) OF 'PK_POLICY_ENDORSEMENT_MAIN' (INDEX (UNIQUE)) (Cost=1 Card=1)
  12    3         SORT (AGGREGATE)
  13   12           FILTER
  14   13             INDEX (RANGE SCAN) OF 'PK_POLICY_ENDORSEMENT_MAIN' (INDEX (UNIQUE)) (Cost=2 Card=2 Bytes=68)

編集3:

上記とまったく同じクエリですが、

ORDER BY 1 ASC

節の場合、結果は期待どおりに取得されます。order by を使用しないこのクエリの PLAN は次のとおりです。

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=18 Card=1 Bytes=114)
   1    0   VIEW (Cost=18 Card=1 Bytes=114)
   2    1     COUNT
   3    2       FILTER
   4    3         TABLE ACCESS (BY INDEX ROWID) OF 'POLICY_ENDORSEMENT_MAIN' (TABLE) (Cost=2 Card=1 Bytes=39)
   5    4           NESTED LOOPS (Cost=16 Card=1 Bytes=130)
   6    5             NESTED LOOPS (Cost=14 Card=1 Bytes=91)
   7    6               TABLE ACCESS (FULL) OF 'POLICY_MAIN' (TABLE) (Cost=14 Card=1 Bytes=82)
   8    6               INDEX (UNIQUE SCAN) OF 'PK_MASTER_UW_LOB_CLASS' (INDEX (UNIQUE)) (Cost=0 Card=1 Bytes=9)
   9    5             INDEX (RANGE SCAN) OF 'PK_POLICY_ENDORSEMENT_MAIN' (INDEX (UNIQUE)) (Cost=1 Card=1)
  10    3         SORT (AGGREGATE)
  11   10           FILTER
  12   11             INDEX (RANGE SCAN) OF 'PK_POLICY_ENDORSEMENT_MAIN' (INDEX (UNIQUE)) (Cost=2 Card=2 Bytes=68)

2 つのプランの唯一の実際の違いは、機能していないプランにはステップ 3 の後に次の 2 つの追加ステップがあることです。これらのステップは、順序付けなしではクエリに存在しないため、正常に機能しています。

予想どおり、ステップ 5 はデータの順序付けが行われるステップです。

   4    3         VIEW (Cost=17 Card=1 Bytes=128)
   5    4           SORT (ORDER BY) (Cost=17 Card=1 Bytes=130)

ステップ 4 は、順序付けのために追加のビューが作成されている可能性があるようです。

これがrownumロジックの動作を妨げる理由は、私がまだ把握しようとしているものです。

どんな助けでも大歓迎です!!

編集 4 - 9i 環境からの元のクエリ プラン

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   VIEW
   2    1     COUNT
   3    2       VIEW
   4    3         SORT (ORDER BY)
   5    4           FILTER
   6    5             TABLE ACCESS (BY INDEX ROWID) OF 'POLICY_MAIN'
   7    6               NESTED LOOPS
   8    7                 NESTED LOOPS
   9    8                   TABLE ACCESS (FULL) OF 'POLICY_ENDORSEMENT_MAIN'
  10    8                   INDEX (RANGE SCAN) OF 'PK_MASTER_UW_LOB_CLASS' (UNIQUE)
  11    7                 INDEX (RANGE SCAN) OF 'PK_POLICY_MAIN' (UNIQUE)
  12    5             SORT (AGGREGATE)
  13   12               FILTER
  14   13                 INDEX (RANGE SCAN) OF 'PK_POLICY_ENDORSEMENT_MAIN' (UNIQUE)
4

4 に答える 4

2

Adam が提案したように、サブクエリは、並べ替えと ROWNUM が適用された後に結果をフィルタリングしています。

PUSH_SUBQヒントを使用して、そのサブクエリを以前に強制的にフィルタリングする必要があると思います。

SELECT * FROM 
( SELECT field1, field2 , field3, field4, field5, field6, field7,
         ROWNUM field8 from 
 ( SELECT PM.POLICY_NO field1
   ,PM.INSURED_CODE field2
   ,PM.INSURED_NAME field3
   ,TO_CHAR(PM.POLICY_EFFECTIVE_DATE,'DD/MM/YYYY') field4
   ,TO_CHAR(PM.POLICY_EXPIRATION_DATE,'DD/MM/YYYY') field5
   ,'' field6
   ,'' field7
   ,'' field8
   FROM POLICY_MAIN PM
   ,POLICY_ENDORSEMENT_MAIN PEM
   ,MASTER_UW_LOB_CLASS MAS
   WHERE PM.POLICY_NO = PEM.POLICY_NO
   AND PM.POLICY_NO LIKE UPPER('%%')
   AND PM.INSURED_CODE LIKE UPPER('%%')
   AND PM.SOURCE_OF_BUSINESS LIKE UPPER('%%')
   AND PM.POLICY_TYPE IS NULL
   AND PM.POLICY_STATUS = 'POST'
   AND PM.POLICY_LOB = MAS.UW_LOB_CODE
   AND MAS.UW_CLASS_CODE LIKE UPPER('AUTO')
   AND PEM.POLICY_ENDORSEMENT_NO =
    (SELECT /*+ PUSH_SUBQ*/
            MAX(PEM2.POLICY_ENDORSEMENT_NO)
     FROM   POLICY_ENDORSEMENT_MAIN       PEM2
     WHERE  PEM.POLICY_NO                 = PEM2.POLICY_NO
     AND    PEM.ENDORSEMENT_STATUS        = 'POST'
     )
   order by 1 ASC
  )
) 
WHERE field8 BETWEEN 21 AND 40

また、ROWNUM から TO_CHAR を削除しました。その範囲比較には数値を使用します。

編集

#2を試してください-代わりにCTEを使用してください:

WITH q AS
( SELECT /*+MATERIALIZE*/
         field1, field2 , field3, field4, field5, field6, field7,
         ROWNUM field8 from 
 ( SELECT PM.POLICY_NO field1
   ,PM.INSURED_CODE field2
   ,PM.INSURED_NAME field3
   ,TO_CHAR(PM.POLICY_EFFECTIVE_DATE,'DD/MM/YYYY') field4
   ,TO_CHAR(PM.POLICY_EXPIRATION_DATE,'DD/MM/YYYY') field5
   ,'' field6
   ,'' field7
   ,'' field8
   FROM POLICY_MAIN PM
   ,POLICY_ENDORSEMENT_MAIN PEM
   ,MASTER_UW_LOB_CLASS MAS
   WHERE PM.POLICY_NO = PEM.POLICY_NO
   AND PM.POLICY_NO LIKE UPPER('%%')
   AND PM.INSURED_CODE LIKE UPPER('%%')
   AND PM.SOURCE_OF_BUSINESS LIKE UPPER('%%')
   AND PM.POLICY_TYPE IS NULL
   AND PM.POLICY_STATUS = 'POST'
   AND PM.POLICY_LOB = MAS.UW_LOB_CODE
   AND MAS.UW_CLASS_CODE LIKE UPPER('AUTO')
   AND PEM.POLICY_ENDORSEMENT_NO =
    (SELECT MAX(PEM2.POLICY_ENDORSEMENT_NO)
     FROM   POLICY_ENDORSEMENT_MAIN       PEM2
     WHERE  PEM.POLICY_NO                 = PEM2.POLICY_NO
     AND    PEM.ENDORSEMENT_STATUS        = 'POST'
     )
   order by 1 ASC
  )
) 
SELECT * from q
WHERE field8 BETWEEN 21 AND 40
于 2010-08-10T03:52:58.537 に答える
1

field8 (ROWNUM に基づく) の計算が遅すぎるように、Oracle がインライン ビューをメイン クエリにマージしているようです。私はそれが起こるのを見たことがありませんが、それが起こっているのであれば、次のような NO_MERGE ヒントを追加してみてください:

SELECT /*+ NO_MERGE(vw) */ * FROM 
( SELECT field1, field2 , field3, field4, field5, field6, field7, to_char(rownum) field8
  FROM
    ( SELECT 
        field1,
        field2,
        field3,
        field4,
        field5,
        field6,
        field7,
        ''
      FROM
      .......REST OF MY COMPLEX INNER QUERY
  )
) vw
WHERE field8 BETWEEN 21 AND 30;

(ちなみに、とにかく WHERE 句で TO_CHAR を数値として扱っているのに、ROWNMUM の TO_CHAR はなぜですか?)

于 2010-08-03T08:50:16.697 に答える
1

これを試して:

SELECT field1, field2 , field3, field4, field5, field6, field7, to_char(rn) field8 from  
 (SELECT PM.POLICY_NO field1 
         ,PM.INSURED_CODE field2 
         ,PM.INSURED_NAME field3 
         ,TO_CHAR(PM.POLICY_EFFECTIVE_DATE,'DD/MM/YYYY') field4 
         ,TO_CHAR(PM.POLICY_EXPIRATION_DATE,'DD/MM/YYYY') field5 
         ,'' field6 
         ,'' field7 
         ,rownum as rn
   FROM POLICY_MAIN PM 
        inner join POLICY_ENDORSEMENT_MAIN PEM 
           on PM.POLICY_NO = PEM.POLICY_NO 
        inner join MASTER_UW_LOB_CLASS MAS 
           on PM.POLICY_LOB = MAS.UW_LOB_CODE 
  WHERE PM.POLICY_NO LIKE UPPER('%%') 
    AND PM.INSURED_CODE LIKE UPPER('%%') 
    AND PM.SOURCE_OF_BUSINESS LIKE UPPER('%%') 
    AND PM.POLICY_TYPE IS NULL 
    AND PM.POLICY_STATUS = 'POST' 
    AND MAS.UW_CLASS_CODE = 'AUTO'
    AND PEM.ENDORSEMENT_STATUS = 'POST'
    AND PEM.POLICY_ENDORSEMENT_NO = 
         (SELECT MAX(PEM2.POLICY_ENDORSEMENT_NO) 
            FROM POLICY_ENDORSEMENT_MAIN PEM2 
           WHERE PEM.POLICY_NO           = PEM2.POLICY_NO 
         ) 
  order by pm.policy_no ASC) 
WHERE rn BETWEEN 21 AND 40 

変更点:

  1. ANSI 構文を使用して結合をフィルターと区別するように再構築された結合。
  2. LIKE UPPER('AUTO')に変更= 'AUTO'
  3. 不要なレベルのネスティングを削除しました。
  4. 式と位置表記を使用するように order by を変更
  5. フィルタリング基準PEM.ENDORSEMENT_STATUS = 'POST'を相関サブクエリからメイン クエリに移動しました。これにより、誤った結果の問題が修正される可能性があります。
  6. 文字式ではなく数値式を使用するように改ページ条件を変更しました。理由は次のとおりです。

    select * from dual where '211' between '21' and '40';

    select * from dual where 211 between 21 and 40;

同じ結果を返さないでください。

于 2010-08-04T14:02:57.600 に答える
0

計画の説明は、問題の特定に役立ちます。トニーが述べたように、内側のクエリを外側のクエリにマージすると、クエリが壊れます。条件 RONUM > 1 が無条件に適用されるクエリはすべて失敗します。

構築しているようなクエリでは、結果セット全体を構築してから、ページの行を除外する必要がある場合があります。内側のクエリで目的の行のキー セットを作成し、外側のクエリに列を追加することを検討してください。rownum で選択するクエリの CARDINALITY ヒントが役立つ場合があります。

「rownum() over (order by 1) rn」を使用して順序を生成してみてください。(順序が 1 とは異なる場合があると思います。) 最初の内部クエリに "/*+ FIRST_ROWS(20) */" を追加します。 詳細については、 http://www.oracle.com/technology/oramag/oracle/07-jan/o17asktom.htmlを参照してください。

于 2010-08-08T21:20:26.710 に答える