テーブルをそれ自体に対して結合し、(トリグラム比較を使用して) ファジー文字列比較を行って、可能な会社名の一致を見つけるクエリを実行しようとしています。私の目標は、あるレコードの会社名 (ref_name フィールド) のトライグラムの類似性が別のレコードの会社名と一致するレコードを返すことです。現在、しきい値を 0.9 に設定しているため、同様の文字列を含む可能性が非常に高い一致のみが返されます。
自己結合によって多くの比較が発生する可能性があることはわかっていますが、できる限りクエリを最適化したいと考えています。すぐに結果を得る必要はありませんが、現在実行しているクエリの実行には 11 時間かかります。
Ubuntu 12.04 サーバーで Postgres 9.2 を実行しています。ref_name フィールド (照合するフィールド) の最大長がわからないので、 に設定しましたvarchar(300)
。テキスト型に設定するとパフォーマンスにまったく影響するのでしょうか、それともパフォーマンスを高速化するために使用するより良いフィールド型があるのでしょうか。私LC_CTYPE
とLC_COLLATE
ロケールはに設定されています"en_US.UTF-8"
クエリを実行しているテーブルは、合計で約 160 万件のレコードで構成されていますが、実行に 11 時間かかるクエリは、その小さなサブセット (約 100k) に基づいています。
テーブル構造:
CREATE TABLE ref_name (
ref_name_id integer,
ref_name character varying(300),
ref_name_type character varying(2),
name_display text,
load_date timestamp without time zone
)
インデックス:
CREATE INDEX ref_name_ref_name_trigram_idx ON ref_name
USING gist (ref_name COLLATE pg_catalog."default" gist_trgm_ops);
CREATE INDEX ref_name_ref_name_trigram_idx_1 ON ref_name
USING gist (ref_name COLLATE pg_catalog."default" gist_trgm_ops)
WHERE ref_name_type::text = 'E'::text;
CREATE INDEX ref_name_ref_name_e_idx ON ref_name
USING btree (ref_name COLLATE pg_catalog."default")
WHERE ref_name_type::text = 'E'::text;
クエリ:
select a.ref_name_id as name_id,a.ref_name AS name,
a.name_display AS name_display,b.ref_name_id AS matched_name_id,
b.ref_name AS matched_name,b.name_display AS matched_name_display
from ref_name a
JOIN ref_name b
ON a.ref_name_id<>b.ref_name_id
AND a.ref_name_id>b.ref_name_id
AND a.ref_name % b.ref_name
WHERE
a.ref_name ~>=~ 'A' and a.ref_name ~<~'B'
AND b.ref_name ~>=~ 'A' and b.ref_name ~<~'B'
AND a.ref_name_type='E'
AND b.ref_name_type='E'
プランの説明:
"Nested Loop (cost=0.00..8560728.16 rows=3598470 width=96)"
" -> Seq Scan on ref_name a (cost=0.00..96556.12 rows=103901 width=48)"
" Filter: (((ref_name)::text ~>=~ 'A'::text) AND ((ref_name)::text ~<~ 'B'::text) AND ((ref_name_type)::text = 'E'::text))"
" -> Index Scan using ref_name_ref_name_trigram_idx_1 on ref_name b (cost=0.00..80.41 rows=35 width=48)"
" Index Cond: ((a.ref_name)::text % (ref_name)::text)"
" Filter: (((ref_name)::text ~>=~ 'A'::text) AND ((ref_name)::text ~<~ 'B'::text) AND (a.ref_name_id <> ref_name_id) AND (a.ref_name_id > ref_name_id))"
サンプル レコードを次に示します。
1652632;"A 123 SYSTEMS";"E";"A 123 SYSTEMS INC";"2014-11-14 00:00:00"
1652633;"A123 SYSTEMS";"E";"A123 SYSTEMS INC";"2014-11-14 00:00:00"
1652640;"A 1 ACCOUSTICS";"E";"A-1 ACCOUSTICS";"2014-11-14 00:00:00"
1652641;"A 1 ACOUSTICS";"E";"A-1 ACOUSTICS";"2014-11-14 00:00:00"
1652642;"A1 ACOUSTICS";"E";"A1 ACOUSTICS INC";"2014-11-14 00:00:00"
1652650;"A 1 A ELECTRICAL";"E";"A-1 A ELECTRICAL INC";"2014-11-14 00:00:00"
1652651;"A 1 A ELECTRICIAN";"E";"A 1 A ELECTRICIAN INC";"2014-11-14 00:00:00"
1652652;"A 1A ELECTRICIAN";"E";"A 1A ELECTRICIAN INC";"2014-11-14 00:00:00"
1652653;"A1 A ELECTRICIAN";"E";"A1 A ELECTRICIAN INC";"2014-11-14 00:00:00"
1691270;"ALBERT GARLATTI";"E";"ALBERT GARLATTI";"2014-11-14 00:00:00"
1691271;"ALBERT GARLATTI CONSTRUCTION";"E";"ALBERT GARLATTI CONSTRUCTION CO";"2014-11-14 00:00:00"
1680892;"AG HOG PITTSBURGH";"E";"AG-HOG PITTSBURGH CO INC";"2014-11-14 00:00:00"
1680893;"AGHOG PITTSBURGH";"E";"AGHOG PITTSBURGH CO";"2014-11-14 00:00:00"
1680928;"AGILE PURSUITS FRACHISING";"E";"AGILE PURSUITS FRACHISING INC";"2014-11-14 00:00:00"
1680929;"AGILE PURSUITS FRANCHISING";"E";"AGILE PURSUITS FRANCHISING INC";"2014-11-14 00:00:00"
1680956;"AGING COMMUNITY COORDINATED ENTERPRISES & SUPPORT";"E";"AGING COMMUNITY COORDINATED ENTERPRISES & SUPPORT";"2014-11-14 00:00:00"
1680957;"AGING COMMUNITY COORDINATED ENTERPRISES & SUPPORTI";"E";"AGING COMMUNITY COORDINATED ENTERPRISES & SUPPORTI";"2014-11-14 00:00:00"
ご覧のとおり、処理を高速化するために要点トライグラム インデックスを作成しました (比較のために、これまでに 2 つの異なるタイプを試しました)。このクエリのパフォーマンスを改善し、11 時間からより管理しやすい時間に短縮する方法について、誰か提案はありますか? 最終的には、この小さなサブセットだけでなく、テーブル全体でこのクエリを実行してレコードを比較したいと考えています。