1

このクエリがあり、適切なインデックスを追加してパフォーマンスを向上させたいと考えています。

DELETE FROM MYTAB1 WHERE MYID1 IN (SELECT MYID2 FROM MYTAB2);

インデックスの構文と、インデックスに必要な設定のタイプに精通していません。同じものを提供してください。ここでの主な問題は、MYTAB1には数百万のレコードがあるため、クエリに多くの時間がかかることです。ただし、MYTAB2には1000レコードしかありません。MYID1はMYTAB1の主キーです

インデックスを作成してみました:

CREATE INDEX IDX_TAB1_ID1 ON MYTAB1(MYID1);

クエリのパフォーマンスにはあまり影響しませんでした。

Explain Planを実行して、これを取得しました。

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------   
| Id  | Operation             | Name          | Rows  | Bytes |TempSpc| Cost (%CPU)|
------------------------------------------------------------------------------------
|   0 | DELETE STATEMENT      |               | 63977 |    11M|       | 62216   (2)|
|   1 |  DELETE               | MYTAB1        |       |       |       |            |
|   2 |   HASH JOIN RIGHT SEMI|               | 63977 |    11M|  7688K| 62216   (2)|
|   3 |    TABLE ACCESS FULL  | MYTAB2        |   437K|  2561K|       |  1189   (2)|
|   4 |    TABLE ACCESS FULL  | MYTAB1        |  3761K|   678M|       | 24718   (4)|   
------------------------------------------------------------------------------------
4

4 に答える 4

3

「MYTAB2には1000レコードしかありません!」

はい。ただし、関連するデータポイントは、MYTAB1のレコードがそれらの1000レコードと一致するレコードの数です。その数字は表全体の何パーセントを表していますか?そして、MYTAB1でのそれらのレコードの分布は何ですか?

MYTAB1の行の20%を削除しようとしている場合、インデックスはパフォーマンスを悪化させるだけです(オプティマイザーがそれを使用するのに十分愚かである場合)。MYTAB1のレコードの0.1%のみを削除するが、それらのレコードがテーブル内のすべてのブロックに分散されている場合も、全表スキャンがより効率的なオプションです。

チューニングには簡単な解決策はありません。それは常に多くの異なる要因の相互作用に依存します。この削除をどのくらいの頻度で実行しますか?Enterprise Editionライセンスと多くの予備のCPU容量がありますか?等々。


MYID1がMYTAB1の主キーである場合、その列にはすでにUNIQUEインデックスが存在するはずです。したがって、新しいインデックスを作成する必要はありません。

あなたがテーブルに整合性制約を適用することを気にしない場所の1つでない限り。それは悪い習慣です。整合性を適用することの明らかな利点とは別に、制約はオプティマイザーに有用な情報を提供し、より良い実行計画につながります。

とにかく、あなたの問題の根本はあなたが今投稿した説明計画で明らかです。MYTAB2には1,000行しかないということですが、オプティマイザーはそれが437万行あると考えているようです。したがって、明らかに、そのテーブルで新しい統計を収集する必要があります。

 exec dbms_state.gather_table_stats(ownname=>user, tabname=>'MYTAB2',estimate_percent=>100)

MYTAB1の統計は正しく、370万行のオーダーがあると思いますか?その場合、インデックス付きルックアップは最もパフォーマンスの高いオプションです。その主キー列に一意のインデックスがあることを確認する必要があります。

 select i.index_name, i.uniqueness
 from user_indexes i
     join user_ind_columns c
         on ( i.index_name =  c.index_name)
 where i.table_name = 'MYTAB1'
 and c.column_name = 'MYID1'

インデックスがない場合は、インデックスを作成する必要があります。

 create unique index mytab1_uidx on mytab1(myid1)
 /

インデックスがあるが一意ではない場合は、おそらくそれを削除して一意のインデックスを作成する必要があります。

間違いがあり、その列が主キーではない場合、つまり重複している場合、CREATEINDEXステートメントは失敗することに注意してください。イベントでは、あなたはあなたが熟考する必要があるより大きな問題を抱えています。


「ただし、[MYTAB2]に含まれる行数は非常に変動します...基本的に、一部の行がテーブルに追加され、一部が削除されてプロセスが続行されます」

このシナリオでは、統計が固定されていると役に立ちます。より良いアイデアは、オプティマイザーに実行時に動的に統計を生成させることです。

exec dbms_state.delete_table_stats(ownname=>user, tabname=>'MYTAB2')
exec dbms_state.lock_table_stats(ownname=>user, tabname=>'MYTAB2')

テーブルの統計を削除してからロックすると、動的サンプリングが有効になっている場合、クエリにテーブルを含めるたびにデータベースがテーブルの統計を生成するように強制されます。これにより、MYTAB2がその時点で保持している行数に関係なく、そのdeleteステートメントを実行するたびにより良い実行プランが生成されます。

詳細をご覧ください。

于 2012-04-27T13:43:38.667 に答える
2

オプティマイザーは、MYTAB2約 437,000 行あると考えているため、テーブル内の行の約 11.6% を削除しようとしています。MYTAB2実際に 1,000 行しかない場合は、統計が古くなっていることを意味しMYTAB2ます。テーブルの統計を収集する場合

BEGIN
  DBMS_STATS.GATHER_TABLE_STATS( <<owner of the table>>,
                                 'MYTAB2' );
END;

その後、クエリ プランを再実行すると、プランは変更されますか? クエリはより速く実行されますか?

MYTAB2次の質問は、なぜオプティマイザが非常に多くの行があると考えたのかということです。これは、グローバル一時テーブルとして宣言されていない一時テーブルですか? 以前はテーブルがはるかに大きかったのに、437,000 行のうち 436,000 行を完全に削除しましたか?

于 2012-04-27T15:10:50.907 に答える
2

これは古典的な問題です。保存したい行を含む新しいテーブルを作成し、new_table の名前を original_table に変更したほうがよい場合があります。

概要:

create table new_table as 
select * from original_table 
where myid1 not in (select myid2 from mytab2)
;

drop table original_table;

rename new_table to original_table;

活動の詳細:

Bulk Delete using CTAS Method
a.  Create table new_table with nologging
CREATE TABLE new_table NOLOGGING (….);
b.  Insert  /*+ APPEND */ into new_table select the records you want to keep from current_table.
c.  Create the indexes on the new_table with NOLOGGING  (*)
d.  Create constraints, grants etc.
e.  Drop current_table.
f.  Rename new_table to current.
g.  Backup the data.
(*) If the data left is so small or there are a lot of dependencies on the table (views, procedures, functions, etc) the following steps can be used instead of c-g above:
c.  Disable constraints on current_table.
d.  Truncate current_table;
e.  Make indexes unusable 
f.  Alter current_table NOLOGGING 
g.  Insert  /*+ APPEND */ into current_table 
select * from new_table; 
h.  commit;
i.  enable constraints
j.  Alter current_Table and indexes to LOGGING
k.  Backup the data
l.  drop table new_table;
于 2012-04-27T11:32:00.757 に答える
1

Oracle10.2でのインデックス作成のドキュメントはこちらです。

次のようなものが必要です。

create index index_name on table_name(column_name);
于 2012-04-27T11:26:08.510 に答える