1

次のような2つのテーブルがあります。

Event
    id
    type
    ... a bunch of other columns

ProcessedEvent
    event_id
    process

に定義されたインデックスがあります

  • イベント(ID)(PK)
  • ProcessedEvent(event_id、process)

1つ目は、アプリケーションのイベントを表します。

2つ目は、特定のイベントが特定のプロセスによってプロセスを取得したという事実を表しています。特定のイベントを処理する必要のあるプロセスは多数あるため、最初のエントリごとに2番目のテーブルに複数のエントリがあります。

処理が必要なすべてのイベントを見つけるために、次のクエリを実行します。

select * // of course we do name the columns in the production code
from Event
where type in ( 'typeA', 'typeB', 'typeC')
and id not in (
    select event_id
    from ProcessedEvent
    where process = :1  
)

統計は最新です

ほとんどのイベントが処理されるので、最適な実行プランは次のようになります。

  • ProcessedEventインデックスの完全なインデックススキャン
  • イベントインデックスのフルインデックススキャン
  • 2つの間の反結合
  • 残りのテーブルへのアクセス
  • フィルター

代わりに、Oracleは次のことを行います

  • ProcessedEventインデックスの完全なインデックススキャン
  • イベントテーブルの全表スキャン
  • イベントテーブルをフィルタリングする
  • 2つのセット間のアンチ結合

インデックスのヒントを使用して、Oracleに次のことを実行させます。

  • ProcessedEventインデックスの完全なインデックススキャン
  • イベントインデックスのフルインデックススキャン
  • イベントテーブルのテーブルアクセス
  • イベントテーブルをフィルタリングする
  • 2つのセット間のアンチ結合

これは本当にばかげた私見です。

だから私の質問は:オラクルが早期のテーブルアクセスを主張する理由は何でしょうか?


追加:パフォーマンスが悪い。Event.IDのみを選択し、必要な行を「手動で」フェッチすることで、パフォーマンスの問題を修正しています。しかしもちろん、それは単なる回避策です。

4

4 に答える 4

2

インデックスはテーブルよりも「薄い」可能性が高いため、全表スキャンはおそらく全表スキャンよりも高速になります。それでも、フルインデックススキャンは完全なセグメント読み取りであり、フルテーブルスキャンとほぼ同じコストになります。

ただし、TABLE ACCESSBYROWIDステップも追加しています。これはコストのかかる手順です。ROWIDアクセスの場合は行ごとに1つの論理IOを取得しますが、全表スキャンの場合はマルチブロックごとに1つの論理IOを取得します(ユーザーによって異なります)。db_file_multiblock_read_count parameter

結論として、オプティマイザーは次のことを計算します。

cost(FULL TABLE SCAN) < cost(FULL INDEX SCAN) + cost(TABLE ACCESS BY ROWID)

更新:FULL TABLE SCANは、FULL INDEX SCANパスよりも早くタイプのフィルターを有効にします(INDEXはイベントのタイプを認識しないため)。したがって、反結合されるセットのサイズが縮小されます(まだ全表スキャンのもう1つの利点)。

于 2010-01-13T14:29:15.107 に答える
0

オプティマイザーは、最初は意味をなさない多くのことを行いますが、それには理由があります。それらは常に正しいとは限りませんが、理解できます。

イベントテーブルは、そのサイズが原因で、ROWIDアクセスよりもフルスキャンの方が簡単な場合があります。テーブル全体を順番に読み取るために必要なIO操作は、ビットとピースを読み取る場合よりも大幅に少ない可能性があります。

パフォーマンスが悪いのですか、それともオプティマイザーがそれを行った理由を尋ねているだけですか?

于 2010-01-13T14:27:11.587 に答える
0

オプティマイザーの動作を説明することはできませんが、私の経験では、「NOT IN」を絶対に避け、代わりにMINUSに置き換えます。

select * from Event
where id in (
  select id from Event where type in ( 'typeA', 'typeB', 'typeC')
 minus
  select id from ProcessedEvent
)

同様の変換を使用すると、クエリのパフォーマンスが桁違いに大きくなります。

于 2010-01-13T14:37:22.453 に答える
0

何かのようなもの:

WITH
  PROCEEDED AS
  (
    SELECT
      event_id
    FROM
      ProcessedEvent
    WHERE
      PROCESS = :1
  )
SELECT
  * // of course we do name the columns in the production code
FROM
  EVENT
LEFT JOIN PROCEEDED P
ON
  p.event_id = EVENT.event_id
WHERE
  type           IN ( 'typeA', 'typeB', 'typeC')
  AND p.event_id IS NULL; -- exclude already proceeded

十分に速く動作する可能性があります(少なくともよりもはるかに高速ですNOT IN)。

于 2013-10-10T13:52:20.703 に答える