15

さまざまなサイズの6つのテーブルの1つから数万行を削除する必要があるプロジェクトがありますが、それらの間に約3,000万行があります。与えられたデータの構造のため、6つのテーブルのどれに削除する必要のある行があるのか​​わからないため、すべてのテーブルに対してすべての削除を実行する必要があります。ID列に対してINDEXを作成して速度を上げようとしましたが、速度が上がる場合は削除できます。

私の問題は、実際に削除を実行する効率的な方法が見つからないように見えることです。テストの目的で、約9400行の単一のテストテーブルに対して7384の削除行を実行しています。Oracle SQL Developerで、考えられるいくつかのクエリソリューションをテストしました。

7384個の個別のDELETEステートメントは203秒かかりました。

delete from TABLE1 where ID=1000001356443294;
delete from TABLE1 where ID=1000001356443296;
etc...

7384の個別のSELECTステートメントには57秒かかりました。

select ID from TABLE1 where ID=1000001356443294
select ID from TABLE1 where ID=1000001356443296
etc...

7384個の個別のDELETE from (SELECT)ステートメントは214秒かかりました。

delete from (select ID from TABLE1 where ID=1000001356443294);
delete from (select ID from TABLE1 where ID=1000001356443296);
etc...

127.4秒かかった場所に7384句がある1つのSELECTステートメント:OR

select ID from TABLE1 where ID=1000001356443294 or ID = 1000001356443296 or ...

74.4秒かかった場所に7384句がある1つのDELETE from (SELECT)ステートメント:OR

delete from (select ID from TABLE1 where ID=1000001356443294 or ID = 1000001356443296 or ...)

最後のものが最速かもしれませんが、さらにテストすると、9000行のテーブルから200,000行のテーブル(最終的なテーブルセットサイズの1%未満)にスケールアップすると、同じステートメントに14分かかります。走る。行ごとに50%以上高速ですが、完全なデータセットに対して実行すると、最大で約1日を推定します。私は、このタスクを実行するために使用したソフトウェアが約20分でそれを実行できることを正当に認めています

だから私の質問は:

  • 削除するより良い方法はありますか?
  • 一連のSELECTステートメント(つまり、2番目のテストなど)を使用して、特定の行がどのテーブルにあるかを検出し、削除クエリを実行する必要がありますか?それでもかなり遅いように見えますが...
  • 削除を高速化するために他にできることはありますか?DBAレベルのアクセス権や知識がありません。
4

4 に答える 4

15

私の質問に答える前に、これは私がそれについて行く方法です:

ステートメントの数とそれらが発行する作業を相対的な観点から最小限に抑えます。

すべてのシナリオは、、などから削除するID()のテーブルがあることを前提としていますPURGE_IDSTABLE_1TABLE_2

非常に大きな削除には、CREATE TABLEASSELECTの使用を検討してください

同時アクティビティがなく、1つ以上のテーブルの行の30%以上を削除する場合は、削除しないでください。create table as select保持したい行でを 実行し、新しいテーブルを古いテーブルと交換します。INSERT /*+ APPEND */ ... NOLOGGINGあなたがそれを買う余裕があれば驚くほど安いです。同時アクティビティがある場合でも、オンラインテーブル再定義を使用してテーブルをインプレースで再構築できる場合があります。

行を削除しないことがわかっているDELETEステートメントを実行しないでください

ID値が最大で6つのテーブルの1つに存在する場合は、削除したIDを追跡し、他のテーブルからそれらのIDを削除しようとしないでください。

CREATE TABLE TABLE1_PURGE NOLOGGING
AS 
SELECT ID FROM PURGE_IDS INNER JOIN TABLE_1 ON PURGE_IDS.ID = TABLE_1.ID;

DELETE FROM TABLE1 WHERE ID IN (SELECT ID FROM TABLE1_PURGE);

DELETE FROM PURGE_IDS WHERE ID IN (SELECT ID FROM TABLE1_PURGE);

DROP TABLE TABLE1_PURGE;

繰り返します。

必要に応じて同時実行性を管理する

もう1つの方法は、PL / SQLを使用してテーブルをループし、行数が制限された削除文を発行することです。これは、削除を実行しているテーブルに対して、挿入/更新/削除の同時負荷が大きい場合に最も適切です。

declare
  l_sql varchar2(4000);
begin
  for i in (select table_name from all_tables 
             where table_name in ('TABLE_1', 'TABLE_2', ...)
             order by table_name);
  loop
    l_sql := 'delete from ' || i.table_name || 
             ' where id in (select id from purge_ids) ' || 
             '   and rownum <= 1000000';
    loop
      commit;
      execute immediate l_sql;
      exit when sql%rowcount <> 1000000;  -- if we delete less than 1,000,000
    end loop;                             -- no more rows need to be deleted!
  end loop;
  commit;
end;
于 2012-04-10T18:52:28.853 に答える
1

削除するすべてのIDをテーブルに保存します。次に、3つの方法があります。1)テーブル内のすべてのIDをループしてから、Xコミット間隔で一度に1行ずつ削除します。Xは100または1000にすることができます。OLTP環境で動作し、ロックを制御できます。

2)OracleBulkDeleteを使用する

3)相関削除クエリを使用します。

コンテキストの切り替えが少なく、解析が少ないため、通常、単一のクエリは複数のクエリよりも高速です。

于 2012-04-10T16:28:57.923 に答える
0

まず、削除中にインデックスを無効にすると便利です。

MERGE INTOステートメントで試してください:
1)IDとTABLE1からの追加の列を使用して一時テーブルを作成し、次のコマンドでテストします

MERGE INTO table1 src
USING (SELECT id,col1
         FROM test_merge_delete) tgt
ON (src.id = tgt.id)
WHEN MATCHED THEN
  UPDATE
     SET src.col1 = tgt.col1
  DELETE
   WHERE src.id = tgt.id
于 2012-04-10T17:57:49.147 に答える
0

私はこのコードを試しましたが、私の場合は正常に機能しています。

DELETE FROM NG_USR_0_CLIENT_GRID_NEW WHERE rowid IN
( SELECT rowid FROM
  (
      SELECT wi_name, relationship, ROW_NUMBER() OVER (ORDER BY rowid DESC) RN
      FROM NG_USR_0_CLIENT_GRID_NEW
      WHERE wi_name = 'NB-0000001385-Process'
  )
  WHERE RN=2
);
于 2017-03-22T18:25:49.320 に答える