2017-11-20 7 views
0

遅い削除のクエリで問題があります。私はスキーマを持っている、例えば、 "デルタ"と言っても、他のテーブルに同じテーブル(同一のカラム&プライマリキー)を持つテーブルを含む "ターゲット"があります。デルタスキーマに表示されるすべての行をターゲットスキーマから削除する必要があります。私は、EXISTSアプローチからDELETE FROM WHERE EXISTSアプローチを使用してこれを試しましたが、それは非常に遅いようです。これはどこからのPostgresqlの遅い削除

CREATE TABLE name2phoneme 
(
    name_id uuid NOT NULL, 
    phoneme_id uuid NOT NULL, 
    seq_num numeric(3,0), 
    CONSTRAINT pk_name2phoneme PRIMARY KEY (name_id, phoneme_id), 
    CONSTRAINT fk_name2phoneme_name_id_2_name FOREIGN KEY (name_id) 
    REFERENCES name (name_id) MATCH SIMPLE 
    ON UPDATE NO ACTION 
    ON DELETE NO ACTION 
    DEFERRABLE INITIALLY DEFERRED, 
    CONSTRAINT fk_name2phoneme_phoneme_id_2_phoneme FOREIGN KEY (phoneme_id) 
    REFERENCES phoneme (phoneme_id) MATCH SIMPLE 
    ON UPDATE NO ACTION 
    ON DELETE NO ACTION 
    DEFERRABLE INITIALLY DEFERRED 
) 

「ターゲット」テーブル(「デルタ」スキーマは、プライマリキーと外部キーを持っていることを除いwhith)両方のテーブルのレイアウトは

DELETE FROM "target".name2phoneme 
WHERE EXISTS(
    SELECT 1 FROM delta.name2phoneme d 
    WHERE name2phoneme.NAME_ID = d.NAME_ID 
    AND name2phoneme.PHONEME_ID = d.PHONEME_ID 
); 

である:ここではクエリの例ですもともとはわずか18M行を超えていましたが、デルタ表には約3.7M行(ターゲットから削除される)が含まれていました。

は、ここで上記のクエリのEXPLAINの出力です:

"Delete on name2phoneme (cost=154858.03..1068580.46 rows=6449114 width=12)" 
" -> Hash Join (cost=154858.03..1068580.46 rows=6449114 width=12)" 
"  Hash Cond: ((name2phoneme.name_id = d.name_id) AND (name2phoneme.phoneme_id = d.phoneme_id))" 
"  -> Seq Scan on name2phoneme (cost=0.00..331148.16 rows=18062616 width=38)" 
"  -> Hash (cost=69000.01..69000.01 rows=3763601 width=38)" 
"    -> Seq Scan on name2phoneme d (cost=0.00..69000.01 rows=3763601 width=38)" 

私は上記のクエリをANALYZE説明しようとしたが、実行には2時間を引き継いだので、私はそれを殺しました。

私はこの操作をどのように最適化できますか?

+1

370万行を削除するとオーバーヘッドが大きくなります。 –

+0

'WHERE(name_id、phoneme_id)IN(SELECT name_id、phoneme_id FROM other_table)を使用してみることができます。これは、あなたが望む行を持つ新しいテーブルを作成し、既存のテーブルを切り捨て、 (name_id、phoneme_id) 'にインデックスを追加しましたが、テーブルのサイズを指定すると、すばらしいものはまったく期待できませんでした。 – jcaron

+0

@jcaron' 'DELETE FROM" target ".name2phoneme WHERE(name_id、 phoneme_id)を(SELECT d.name_id、d.phoneme_id FROM "delta" .name2phoneme d); 'に置き換えて、WHERE EXISTSアプローチと同じコストにしました。 –

答えて

0

あなたはこれらのアプローチのいずれかを試してみました:

DELETE 
FROM "target".name2phoneme t 
    USING delta.name2phoneme d 
WHERE t.NAME_ID = d.NAME_ID 
     AND t.PHONEME_ID = d.PHONEME_ID    
; 

またはWITHを使用しますが、PostgresはCTEを具現ないので、私は、これは必要性のあなたの規模で賢明です自信を持っていませんよ。

WITH cte AS (
     SELECT t.name_id, t.phoneme_id 
     FROM "target".name2phoneme t 
     INNER JOIN delta.name2phoneme d ON t.NAME_ID = d.NAME_ID 
          AND t.PHONEME_ID = d.PHONEME_ID    
    ) 
DELETE FROM "target".name2phoneme t 
    USING cte d 
WHERE t.NAME_ID = d.NAME_ID 
     AND t.PHONEME_ID = d.PHONEME_ID    
; 
1

各行をルックアップしてから、その行をロギングして削除するために、370万行を削除するのは非常に時間がかかります。ダーティページ、ロギング、およびキャッシュミスのすべてを考えてみると、索引の更新だけでなく、思いもよばないものです。

そのため、このようなものは、はるかに高速になります

create temporary table temp_n2p as 
    select n2p.* 
    from "target".name2phoneme n2p 
    where not exists (select 1 
         from delta.name2phoneme d 
         where n2p.NAME_ID = d.NAME_ID and 
          n2p.PHONEME_ID = d.PHONEME_ID 
        ); 

truncate table "target".name2phoneme; 

insert into "target".name2phoneme 
    select * 
    from temp_n2p; 

あなたはまた、切り捨ての前にインデックスを削除してから、後でそれらを再作成する必要があります。

+0

意味があります。私はこのアプローチを試みます、ありがとう。しかし、それが存在しない場所にあってはいけませんか? –

+0

@WouterVanDaele。 。 。はい、それは正しいロジックでしょう。 –

関連する問題