7

問題

これら 2 つの Oracle Syntax Update クエリのわずかな違いのように見えるものが、根本的に異なる実行計画を引き起こしている理由を理解しようとしています。

クエリ 1:

UPDATE sales s
   SET status = 'DONE', trandate = sysdate
 WHERE EXISTS (Select *    
 FROM tempTable tmp
     WHERE s.key1 = tmp.key1
       AND s.key2 = tmp.key2
       AND s.key3 = tmp.key3)

クエリ 2:

UPDATE sales s
   SET status = 'DONE', trandate = sysdate
 WHERE EXISTS (Select rownum    
 FROM tempTable tmp
     WHERE s.key1 = tmp.key1
       AND s.key2 = tmp.key2
       AND s.key3 = tmp.key3)

ご覧のとおり、2 つの唯一の違いは、クエリ 2 のサブクエリがすべての行の値ではなく、rownum を返すことです。

これら 2 つの実行計画は、これ以上の違いはありません。

  • Query1 - 両方のテーブルから合計結果を取得し、並べ替えとハッシュ結合を使用して結果を返します。これは、有利な 2,346 コストでうまく機能します (EXISTS 句と結合サブクエリを使用しているにもかかわらず)。

  • クエリ 2 - 両方のテーブル結果も取得しますが、カウントとフィルターを使用して同じタスクを実行し、驚くべき 77,789,696 コストの実行プランを返します! 彼のクエリは私にかかっているだけなので、実際にはこれが同じ結果を返すとは確信していません(そうあるべきだと思いますが)。

Exists 句についての私の理解では、これはメイン テーブルの行ごとに実行される単純なブール値チェックです。EXISTS 条件で単一の行が返されるか、100,000 行が返されるかは問題ではありません...実行されている行に対して結果が返された場合、存在チェックに合格しています。では、サブクエリの SELECT ステートメントが何を返すかが問題になるのはなぜでしょうか?

- - - - - - - - - - 編集 - - - - - - - - - - -

リクエストごとに、以下は TOAD で実行している実行プランです... 上記の例ではテーブル名を簡単に編集したことに注意してください。

また、言及する必要がありましたが、この時点では2つのテーブルのどちらにもインデックスがありません..まだtempTableに追加しておらず、フィールドとデータのみを含みインデックスを含まないsalesテーブルの安価なコピーでテストしています、制約またはセキュリティ。

みんな助けてくれてありがとう!

クエリ 1 実行計画

Query1 実行計画

クエリ 2 実行計画

Query2 実行計画

------------------------------------------------

質問

1) rownum の呼び出しによって実行計画が変更されたのはなぜですか?

2) 信じられないほど非効率なフィルターの原因は何ですか?

3) この変更の原因となっている Exists 句の動作方法に根本的な何かが欠けていますか?

4

2 に答える 2

8

実際のクエリ プランを投稿すると、非常に役立ちます。

ただし、一般に、オプティマイザが を含むサブクエリを検出するとrownum、クエリを変換してサブクエリの結果をメイン クエリにマージする機能が大幅に制限されます。これは、結果に影響を与える可能性があるためです。これは、オプティマイザによって選択された計画よりも効率的である場合に、Oracle にサブクエリを強制的に実行させる簡単な方法です。ただし、この場合、クエリをより効率的にする変換ステップをオプティマイザが先送りする原因になっている可能性があります。

時折、誰かが次のようなクエリを実行するのを目にします。

SELECT b.*
  FROM (SELECT <<columns>>
          FROM driving_table
         WHERE <<conditions>>) a,
       b
 WHERE a.id = b.id

サブクエリに arownumを追加しますa

SELECT b.*
  FROM (SELECT <<columns>>, rownum
          FROM driving_table
         WHERE <<conditions>>) a,
       b
 WHERE a.id = b.id

結合を実行する前にオプティマイザにaサブクエリを評価させるため。もちろん、通常、オプティマイザは、より効率的である場合、デフォルトでこれを行う必要があります。しかし、オプティマイザが間違いを犯した場合rownum、計画を強制するための適切なヒントのセットを見つけたり、根本的な問題を掘り下げて正しい解決策を見つけたりするよりも、追加する方が迅速な場合があります。

もちろん、リストに唯一の使用があるサブクエリがある特定のケースでは、WHERE EXISTS私たち人間は、オプティマイザが使用しようとするクエリ変換ステップを妨げてはならないことを検出できます。ただし、オプティマイザーはおそらく、次のような関数を参照するサブクエリを完全に実行する必要があるというより一般的なルールを使用しています (これは、正確な Oracle のバージョンやオプティマイザーの設定に依存する場合があります)。したがって、オプティマイザーは、追加した がクエリの結果に影響を与える可能性がないことを認識できるほど賢くないため、現実的に余分な作業を行っています。rownumSELECTrownumrownumrownum

于 2013-12-06T22:44:37.817 に答える
0

質問ですが、このクエリの実行計画は次のとおりです。

UPDATE sales s
   SET status = 'DONE', trandate = sysdate
 WHERE EXISTS (Select NULL
 FROM tempTable tmp
     WHERE s.key1 = tmp.key1
       AND s.key2 = tmp.key2
       AND s.key3 = tmp.key3);

式に必要なものを視覚化しEXISTS (...)ます - 実際には何もありません! すでに述べたように、Oracleは、サブクエリで返されたものではなく、何かが返されたかどうかを確認する必要があります。

于 2013-12-07T15:54:32.887 に答える