web-dev-qa-db-ja.com

7億行のOracleテーブルで実行される更新SQLを最適化する方法

UPDATE [TABLE] SET [FIELD]=0 WHERE [FIELD] IS NULL

[TABLE]は、7億行を超えるOracleデータベーステーブルです。 SQLの実行を6時間実行した後、キャンセルしました。

パフォーマンスを向上させる可能性のあるSQLヒントはありますか?またはそれをスピードアップする他の解決策はありますか?

EDIT:このクエリは一度実行され、その後は二度と実行されません。

10
b.roth

まず第一に、それは1回限りのクエリですか、それとも繰り返しクエリですか?一度だけ実行する必要がある場合は、クエリを並列モードで実行することを検討することをお勧めします。とにかくすべての行をスキャンする必要があります。ワークロードをROWID(日曜大工の並列処理)の範囲で自分で分割するか、Oracleの組み込み機能を使用することができます。

頻繁に実行し、このクエリを最適化する場合、field列がNULLの行数は、最終的には行の総数に比べて少なくなります。その場合、インデックスは物事をスピードアップする可能性があります。 Oracleは、すべてのインデックス付き列を持つ行にNULLとしてインデックスを付けないため、fieldのインデックスはクエリで使用されません(fieldがNULLであるすべての行を検索するため)。

どちらか:

  • _(FIELD, 0)_にインデックスを作成すると、_0_はNULL以外の疑似列として機能し、すべての行がテーブルにインデックス付けされます。
  • _(CASE WHEN field IS NULL THEN 1 END)_に関数ベースのインデックスを作成します。これにより、NULLである行のみにインデックスが付けられます(したがって、インデックスは非常にコンパクトになります)。その場合、クエリを書き直す必要があります。

    UPDATE [TABLE] SET [FIELD]=0 WHERE (CASE WHEN field IS NULL THEN 1 END)=1

編集:

これは1回限りのシナリオであるため、PARALLELヒントを使用することをお勧めします。

_SQL> EXPLAIN PLAN FOR
  2  UPDATE /*+ PARALLEL(test_table 4)*/ test_table
  3     SET field=0
  4   WHERE field IS NULL;

Explained

SQL> select * from table( dbms_xplan.display);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 4026746538
--------------------------------------------------------------------------------
| Id  | Operation             | Name       | Rows  | Bytes | Cost (%CPU)| Time
--------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT      |            | 22793 |   289K|    12   (9)| 00:00:
|   1 |  UPDATE               | TEST_TABLE |       |       |            |
|   2 |   PX COORDINATOR      |            |       |       |            |
|   3 |    PX SEND QC (RANDOM)| :TQ10000   | 22793 |   289K|    12   (9)| 00:00:
|   4 |     PX BLOCK ITERATOR |            | 22793 |   289K|    12   (9)| 00:00:
|*  5 |      TABLE ACCESS FULL| TEST_TABLE | 22793 |   289K|    12   (9)| 00:00:
--------------------------------------------------------------------------------
_
10
Vincent Malgrat

他のユーザーがテーブルの同じ行を同時に更新していますか?

もしそうなら、あなたは(ロックを待っている)多くの並行性の問題にぶつかる可能性があり、それをより小さなトランザクションに分割する価値があるかもしれません。

DECLARE
  v_cnt number := 1;
BEGIN
 WHILE v_cnt > 0 LOOP
   UPDATE [TABLE] SET [FIELD]=0 WHERE [FIELD] IS NULL AND ROWNUM < 50000;
   v_cnt := SQL%ROWCOUNT;
   COMMIT;
 END LOOP;
END;
/

ROWNUM制限が小さいほど、発生する同時実行/ロックの問題は少なくなりますが、テーブルスキャンに費やす時間は長くなります。

5
Gary Myers

ヴィンセントはすでにあなたの質問に完全に答えました、しかし私はこの行動の背後にある「なぜ」について興味があります。すべてのNULLを0に更新するのはなぜですか?

よろしく、ロブ。

3
Rob van Wijk

いくつかの提案:

  1. UPDATEステートメントを実行する前にFIELDを含むインデックスをすべて削除し、後で再度追加します。

  2. これを行うために、1000行または10000行ごとにコミットするPL/SQLプロシージャを記述します。

お役に立てれば。

1
Bob Jarvis

ALTERテーブルを使用して列の「DEFAULT」値を0に設定することにより、更新せずに同じ結果を得ることができます。

0
James Anderson