2

完了するまでに時間がかかる 2 つの削除ステートメントがあります。where句の列にはいくつかのインデックスがあります。

重複とは何ですか? 2 つ以上のレコードの列 id、cid、type、trefid、ordrefid、amount、および paydt に同じ値がある場合、重複があります。

DELETE は約 100 万件のレコードを削除します。

より速くするために、何らかの方法で書き直すことはできますか。

DELETE FROM TABLE1 A WHERE loaddt < (
    SELECT max(loaddt) FROM TABLE1 B
    WHERE 
    a.id=b.id and
    a.cid=b.cid and
    NVL(a.type,'-99999') = NVL(b.type,'-99999') and
    NVL(a.trefid,'-99999')=NVL(b.trefid,'-99999') and
    NVL(a.ordrefid,'-99999')= NVL(b.ordrefid,'-99999') and
    NVL(a.amount,'-99999')=NVL(b.amount,'-99999') and
    NVL(a.paydt,TO_DATE('9999-12-31','YYYY-MM-DD'))=NVL(b.paydt,TO_DATE('9999-12-31','YYYY-MM-DD'))
);

    COMMIT;

DELETE FROM TABLE1 a where rowid > (
    Select min(rowid) from TABLE1 b
    WHERE 
    a.id=b.id and
    a.cid=b.cid and
    NVL(a.type,'-99999') = NVL(b.type,'-99999') and
    NVL(a.trefid,'-99999')=NVL(b.trefid,'-99999') and
    NVL(a.ordrefid,'-99999')= NVL(b.ordrefid,'-99999') and
    NVL(a.amount,'-99999')=NVL(b.amount,'-99999') and
    NVL(a.paydt,TO_DATE('9999-12-31','YYYY-MM-DD'))=NVL(b.paydt,TO_DATE('9999-12-31','YYYY-MM-DD'))
);

commit;

プランの説明:

DELETE  TABLE1         

    HASH JOIN 1296491 
    Access Predicates 

        AND 
        A.ID=ITEM_1 
        A.CID=ITEM_2 
        ITEM_3=NVL(TYPE,'-99999') 
        ITEM_4=NVL(TREFID,'-99999') 
        ITEM_5=NVL(ORDREFID,'-99999') 
        ITEM_6=NVL(AMOUNT,(-99999)) 
        ITEM_7=NVL(PAYDT,TO_DATE(' 9999-12-31 00:00:00', 'syyyy-mm-dd hh24:mi:ss')) 

    Filter Predicates 
        LOADDT<MAX(LOADDT)

    TABLE ACCESS  TABLE1     FULL    267904 
    VIEW VW_SQ_1         690385 
    SORT GROUP BY    690385 
        TABLE ACCESS TABLE1      FULL    267904 
4

3 に答える 3

2

テーブルの大きさは?削除された行の数が最大 12% の場合は、インデックスについて考えることができます。どうにかしてテーブルを分割できますか?たとえば、週ごとに分割してから、実際の週だけをスキャンできますか?

たぶん、これはより効率的かもしれません。集計関数を使用している場合、オラクルは関連するすべての行 (この場合はフルスキャン) をウォークスルーする必要がありますが、exists を使用すると、最初の出現が見つかったときに停止します。(もちろん、where 句のすべての列に関数ベースの (NVL のため) インデックスが 1 つある場合、クエリははるかに高速になります)

DELETE FROM TABLE1 A 
WHERE exists (
SELECT 1 
FROM TABLE1 B
WHERE 
A.loaddt != b.loaddt
a.id=b.id and
a.cid=b.cid and
NVL(a.type,'-99999') = NVL(b.type,'-99999') and
NVL(a.trefid,'-99999')=NVL(b.trefid,'-99999') and
NVL(a.ordrefid,'-99999')= NVL(b.ordrefid,'-99999') and
NVL(a.amount,'-99999')=NVL(b.amount,'-99999') and
NVL(a.paydt,TO_DATE('9999-12-31','YYYY-MM-DD'))=NVL(b.paydt,TO_DATE('9999-12-31','YYYY-MM-DD'))
);
于 2012-10-01T21:47:40.870 に答える
1

同意しない人もいるかもしれませんが、私は大規模で長時間実行される削除を手続き的に実行することを支持しています。私の見解では、進行状況を制御および追跡する方がはるかに簡単です (そして、DBA はあなたをより気に入るはずです ;-) また、重複を識別するために table1 をそれ自体に結合する必要がある理由がわかりません (そして、もしあなたが遭遇したことがあるなら、私は興味があります現在のアプローチではスナップショットが古すぎる問題)。また、複数の削除ステートメントは必要ありません。すべての重複は 1 つのプロセスで処理する必要があります。最後に、毎週重複を頻繁に再導入している理由を確認し、ロード プロセスを変更する必要があります (すべての挿入ではなく、マージ/アップサートを実行するなど)。

つまり、次のようなことを試すことができます。

-- first create mat view to find all duplicates
create materialized view my_dups_mv
tablespace my_tablespace
build immediate
refresh complete on demand
as
select id,cid,type,trefid,ordrefid,amount,paydt, count(1) as cnt
from table1
group by id,cid,type,trefid,ordrefid,amount,paydt
having count(1) > 1;

-- dedup data (or put into procedure and schedule along with mat view refresh above)
declare
  -- make sure my_dups_mv is refreshed first
  cursor dup_cur is
  select * from my_dups_mv;

  type duprec_t is record(row_id rowid);
  duprec duprec_t;
  type duptab_t is table of duprec_t index by pls_integer;
  duptab duptab_t;

  l_ctr pls_integer := 0;
  l_dupcnt pls_integer := 0;
begin
  for rec in dup_cur
  loop
    l_ctr := l_ctr + 1;

    -- assuming needed indexes exist
    select rowid
    bulk collect into duptab
    from table1
    where id = rec.id
    and cid = rec.cid
    and type = rec.type
    and trefid = rec.trefid
    and ordrefid = rec.ordrefid
    and amount = rec.amount
    and paydt = rec.paydt
    -- order by whatever makes sense to make the "keeper" float to top
    order by loaddt desc
    ;

    for i in 2 .. duptab.count
    loop
      l_dupcnt := l_dupcnt + 1;
      delete from table1 where rowid = duptab(i).row_id;
    end loop;

    if (mod(l_ctr, 10000) = 0) then
      -- log to log table here (calling autonomous procedure you'll need to implement)
      insert_logtable('Table1 deletes', 'Commit reached, deleted ' || l_dupcnt || ' rows');
      commit;
    end if;

  end loop;
  commit;
end;

進行状況については、ログ テーブルを確認してください。

于 2012-10-01T19:52:59.513 に答える
0

1.パラレル

alter session enable parallel dml;

DELETE /*+ PARALLEL */ FROM TABLE1 A WHERE loaddt < (
...

健全なサーバー構成である Enterprise Edition があり、11g を使用しているとします。11g を使用していない場合、並列構文は少し異なります。

2.メモリ要件を減らす

計画はハッシュ結合を示していますが、これはおそらく良いことです。しかし、有用なフィルターがなければ、Oracle はテーブル全体をハッシュする必要があります。( a のみを使用する Tbone のクエリは、GROUP BYより見栄えがよく、より高速に実行される可能性があります。ただし、テーブル全体をソートまたはハッシュしようとすると、おそらく同じ問題が発生する可能性があります。)

ハッシュがメモリに収まらない場合は、ディスクに書き込む必要がありますが、これは非常に遅くなる可能性があります。このクエリを毎週実行するため、すべての行を確認する必要があるテーブルは 1 つだけです。正確にいつ実行されるかに応じて、クエリの最後に次のようなものを追加できます) where b.loaddt >= sysdate - 14。これにより、一時テーブルスペースへの書き込み量が大幅に削減される可能性があります。また、jakub.petr が提案するようなパーティショニング戦略を使用すると、読み取り IO も削減される可能性があります。

3.アクティブレポート

クエリの動作を正確に知りたい場合は、アクティブ レポートを実行します。

select dbms_sqltune.report_sql_monitor(sql_id => 'YOUR_SQL_ID_HERE', type => 'active')
from dual;

(出力を .html ファイルに保存し、ブラウザーで開きます。)

于 2012-10-02T04:42:27.877 に答える