2017-06-02 12 views
0

私はPostgreSQL 9.6.2を実行しており、約2,900,000行の7列の表を持っています。この表は一時的なものであり、サブジェクトの重複排除プロセスの一部です。これは、新しいID(s_id_new)を、異なるルールのセットに基づいて同一のサブジェクトに割り当てることを目的としています。合計で、データの類似した、わずかに異なるサブセット/異なるWHERE条件/異なる結合列について、毎回約10-12回の内部結合を実行します。多くの列のPostgresql内部結合(特に自己結合)

今のところ、クエリは非効率的で、完了しなかった(2時間後にキャンセルする必要がありました)。

私は最適化のために、データのサブセット(50 000行)を作成しました。

\d subject_subset; 
    Column  |   Type   | Modifiers 
----------------+------------------------+----------- 
s_id   | text     | 
surname_clean | character varying(20) | 
name_clean  | character varying(20) | 
fullname_clean | character varying(100) | 
id1   | character varying(20) | 
id2   | character varying(20) | 
id3   | character varying(20) | 
s_id_new  | character varying(20) | 
Indexes: 
    "subject_subset_s_id_new_idx" btree (s_id_new) 

私は私の知る限り、それは完全なテーブルがソートされたときにたくさんのスペースを消費し、問題を引き起こしているサブクエリのSORTだ見ることができるように

select s_id_new, max(I_s_id) as s_id_deduplicated 
from (select a.*, b.s_id_new as I_s_id 
       from public.subject_subset a 
       inner join public.subject_subset b on a.surname_clean=b.surname_clean 
       and a.id2=b.id2 
       where 
        a.id1 is null 
        and a.id2 is not null 
        and a.surname_clean is not null) h 
group by s_id_new; 



The result of the EXPLAIN ANALYZE: 
https://explain.depesz.com/s/7knH 

"GroupAggregate (cost=5616.65..5620.39 rows=142 width=90) (actual time=32542.127..46938.858 rows=2889 loops=1)" 
" Group Key: a.s_id_new" 
" -> Sort (cost=5616.65..5617.42 rows=310 width=116) (actual time=32542.116..43194.626 rows=18356220 loops=1)" 
"  Sort Key: a.s_id_new" 
"  Sort Method: external merge Disk: 531760kB" 
"  -> Hash Join (cost=1114.72..5603.82 rows=310 width=116) (actual time=13.159..4892.011 rows=18356220 loops=1)" 
"    Hash Cond: (((b.surname_clean)::text = (a.surname_clean)::text) AND ((b.id2)::text = (a.id2)::text))" 
"    -> Seq Scan on subject_subset b (cost=0.00..1111.00 rows=50000 width=174) (actual time=0.011..10.775 rows=50000 loops=1)" 
"    -> Hash (cost=1111.00..1111.00 rows=248 width=174) (actual time=13.137..13.137 rows=15044 loops=1)" 
"     Buckets: 16384 (originally 1024) Batches: 1 (originally 1) Memory Usage: 1151kB" 
"     -> Seq Scan on subject_subset a (cost=0.00..1111.00 rows=248 width=174) (actual time=0.005..9.330 rows=15044 loops=1)" 
"       Filter: ((id1 IS NULL) AND (id2 IS NOT NULL) AND (surname_clean IS NOT NULL))" 
"       Rows Removed by Filter: 34956" 
"Planning time: 0.236 ms" 
"Execution time: 47013.839 ms" 

を最適化しようとしているクエリ、私はそれを最適化する方法を理解することはできません。

パフォーマンスをわずかに改善したのは、新しい整数IDにdense_rankを割り当てることだけでしたが、それだけでは不十分です。

+0

この特定のクエリが達成しようとしている目標を言葉で説明すると役立ちます。それ以外の場合は、クエリに基づいてタスクを推測しようとする必要があります。 –

+0

このクエリは、企業や自然人などの被験者に同じIDを割り当てるよう重複排除することを目的としています。同じ文書IDを持つ2人のJonh Smithsがデータベース(s_id)で異なるIDを持っている - >コードは、新しいID =彼が現在持っているs_idの最大値をそれらに割り当てます。補助データ(アドレス、電話など)に補助データが使用されることもありますが、アイデアは変わりません。 – Dominix

答えて

0

大きなソートがあなたを殺しています。

  1. 実行ANALYZE subject_subsetテーブルのテーブルの統計情報を収集する:

    私は3つの提案を持っています。 一時テーブルの統計情報は自動的には収集されません。

    おそらくそれを改善するには十分です。

  2. ネストされたループ結合に役立つインデックスを(id2, surname_clean, s_id_new)で試してみましょう(これは速いのかどうかわかりません)。あなたが横試みることができる

    は、ネストされたループが高価になるだろう参加

    SELECT a.s_id_new, 
         max(b.i_s_id) AS s_id_deduplicated 
    FROM subject_subset a 
        CROSS JOIN LATERAL (SELECT s_id_new AS i_s_id 
             FROM subject_subset 
             WHERE a.surname_clean = surname_clean 
             AND a.id2 = id2 
             ORDER BY s_id_new DESC 
             LIMIT 1 
            ) b 
    GROUP BY a.s_id_new; 
    

    のように参加しますが、ソートは高速である必要があります。ハッシュと

  3. スティック参加しますが、行の数を減らす:

    SELECT a.s_id_new, 
         max(b.i_s_id) AS s_id_deduplicated 
    FROM subject_subset a 
        JOIN (SELECT surname_clean, id2, 
           max(s_id_new) AS i_s_id 
         FROM subject_subset 
         GROUP BY surname_clean, id2 
         ) b 
         USING (surname_clean, id2) 
    WHERE a.id1 IS NULL 
        AND a.id2 IS NOT NULL 
        AND a.surname_clean IS NOT NULL 
    GROUP BY a.s_id_new; 
    

    たぶん(surname_clean, id2)上のインデックスがわからなく、助けることができます。