2

私は次のようなクエリで更新を実行しています:

UPDATE (SELECT     h.m_id,
                   m.id
        FROM       h
        INNER JOIN m
        ON         h.foo = m.foo)
SET    m_id = id
WHERE  m_id IS NULL

いくつかの情報:

  • テーブルhは約500万行です
  • テーブルのすべての行には、次の値hがありますNULLm_id
  • テーブルmは約50万行です
  • m_idon tablehは、ontableを指すインデックス付き外部キーです。idm
  • idテーブル上mの主キーです
  • とにインデックスがありm.fooますh.foo

このEXPLAIN PLANクエリのは、ハッシュ結合と全表スキャンを示していましたが、私はDBAではないため、実際にはうまく解釈できません。

クエリ自体は数時間実行され、完了しませんでした。私はそれがほんの数分で完了すると思っていたでしょう。また、次のクエリの書き換えを試みました。

UPDATE h
SET    m_id = (SELECT id
               FROM   m
               WHERE  m.foo = h.foo)
WHERE  m_id IS NULL

これEXPLAIN PLANについては、ROWIDルックアップとインデックスの使用について説明しましたが、完了せずに数時間続きました。また、このようなクエリでは、外部クエリの述語からのすべての結果に対してサブクエリが実行されるという印象を常に受け​​ていたため、とにかくこの書き換えではパフォーマンスが非常に低下すると予想されます。

私のアプローチに何か問題がありますか、または私の問題はインデックス、テーブルスペース、またはその他のクエリに関連しない要因に関連していますか?

編集:

私はまた、次のような単純なカウントクエリからのひどいパフォーマンスを持っています:

SELECT COUNT(*)
FROM   h
WHERE  m_id IS NULL

これらのクエリには、約30秒から場合によっては約30分(!)かかります。

ロックがないことに気づいていますが、これらのテーブルのテーブルスペースは現在99.5%の使用率(最大6MBの空き容量)になっています。インデックスが使用されている限り、これは問題ではないと言われましたが、わかりません...

4

5 に答える 5

3

いくつかのポイント:

  • Oracleは値にインデックスを付けませ(グローバルにnull以外のタプルの一部であるaにインデックスを付けますが、それだけです)。NULLNULL

  • Oracleは、とHASH JOINの両方のサイズのためにを採用しています。これは、パフォーマンスの面でおそらく最良のオプションです。hm

  • 2つ目は、Oracleにインデックスを使用させるUPDATE 可能性がありますが、Oracleは通常、サブクエリのマージについて賢明です。そしてそれはとにかく悪い計画になるでしょう。

  • スキーマの最近の妥当な統計はありますか?オラクルは本当にまともな統計を必要としています。

  • 実行プランでは、?の最初のテーブルはHASH JOINどれですか。最高のパフォーマンスを得るには、テーブルを小さくする必要があります(mあなたの場合)。カーディナリティの統計が適切でない場合、Oracleは混乱します。ヒントを使用してOracleに固定カーディナリティを想定させることができますcardinality。これは、Oracleがより良い計画を立てるのに役立つ場合があります。

たとえば、最初のクエリでは次のようになります。

UPDATE (SELECT /*+ cardinality(h 5000000) cardinality(m 500000) */
               h.m_id, m.id 
        FROM h 
        INNER JOIN m 
        ON h.foo = m.foo) 
SET m_id = id 
WHERE m_id IS NULL
  • Oracleでは、FULL SCANはテーブル内のすべてのレコードを読み取るだけでなく、基本的に、使用される最大値( Oracleドキュメントの最高水準点)までに割り当てられたすべてのストレージを読み取ります。したがって、削除された行がたくさんある場合は、テーブルをクリーンアップする必要があります。問題のテーブルに2億5000万の削除された行があったため、空のテーブルで30秒以上かかるのを見ました。その場合は、DBAを使用して特定のケースを分析することをお勧めします。これにより、DBAは削除された行からスペースを再利用し、最高水準点を下げることができます。SELECT COUNT(*)
于 2012-09-20T07:48:17.330 に答える
2

私が覚えている限りWHERE m_id IS NULL、NULL値にはインデックスを付けることができないため、aは全表スキャンを実行します。

全表スキャンとは、エンジンがWHERE条件を評価するためにテーブル内のすべてのレコードを読み取る必要があり、インデックスを使用できないことを意味します。

の場合、null以外の値に設定された仮想列を追加し、m_id IS NULLこの列にインデックスを付けて、WHERE条件でこの列を使用することができます。

次に、WHERE条件をUPDATEステートメントから副選択に移動することもできます。これにより、ステートメントが高速化される可能性があります。

JOINは高価なので、次のINNER JOIN m ON h.foo = m.fooように書き直します。

WHERE h.foo IN (SELECT m.foo FROM m WHERE m.foo IS NOT NULL)

役立つかもしれません。

于 2012-09-20T05:37:59.210 に答える
1

大きなテーブルの場合、多くの場合、MERGEはUPDATEよりもはるかに高速です。これを試してください(テストされていません):

MERGE INTO h USING
(SELECT     h.h_id,
            m.id as new_m_id
        FROM       h
        INNER JOIN m
        ON         h.foo = m.foo
 WHERE h.m_id IS NULL       
) new_data
ON (h.h_id = new_data.h_id)
WHEN MATCHED THEN
  UPDATE SET h.m_id = new_data.new_m_id;
于 2012-09-20T08:48:29.443 に答える
0

たとえば、テーブルを繰り返し更新し、それに応じて条件を追加し、where h.date_created > sysdate-30終了後に同じクエリを実行して、条件を次のように変更し ますwhere h.date_created between sysdate-30 and sysdate-60date_createdに ?例えば:WHERE m.foo = h.foo AND m.foo between 1 and 10

この更新のコストが高い理由を説明できるのはの結果だけplanですが、知識に基づいて推測すると、両方のテーブルが非常に大きく、多くのNULL値と多くの一致があります(m.foo = h.foo)...

于 2012-09-20T05:27:29.150 に答える
0

文書化されていないヒント/*+ BYPASS_UJVC*/を試してください。動作する場合は、m.fooにUNIQUE/PK制約を追加します。

于 2012-09-20T06:57:16.467 に答える