したがって、このテーブルには620万件のレコードがあり、列の類似性を使用して検索クエリを実行する必要があります。クエリは次のとおりです。
SELECT "lca_test".* FROM "lca_test"
WHERE (similarity(job_title, 'sales executive') > 0.6)
AND worksite_city = 'los angeles'
ORDER BY salary ASC LIMIT 50 OFFSET 0
Where(year = X、worksite_state = N、status = 'certified'、visa_class = Z)にさらに条件を追加できます。
これらのクエリの一部を実行すると、30秒を超える非常に長い時間がかかる場合があります。時々1分以上。
EXPLAIN ANALYZE
前述のクエリの結果は次のとおりです。
Limit (cost=0.43..42523.04 rows=50 width=254) (actual time=9070.268..33487.734 rows=2 loops=1) -> Index Scan using index_lca_test_on_salary on lca_test (cost=0.43..23922368.16 rows=28129 width=254) (actual time=9070.265..33487.727 rows=2 loops=1) >>>> Filter: (((worksite_city)::text = 'los angeles'::text) AND (similarity((job_title)::text, 'sales executive'::text) > 0.6::double precision)) >>>> Rows Removed by Filter: 6330130 Total runtime: 33487.802 ms Total runtime: 33487.802 ms
列にインデックスを付けて高速に実行する方法を理解できません。
編集:これはpostgresバージョンです:
X86_64-unknown-linux-gnu上のPostgreSQL 9.3.5、gccでコンパイル(Debian 4.7.2-5)4.7.2、64ビット
以下はテーブルの定義です。
Table "public.lca_test"
Column | Type | Modifiers | Storage | Stats target | Description
------------------------+-------------------+-------------------------------------------------------+----------+--------------+-------------
id | integer | not null default nextval('lca_test_id_seq'::regclass) | plain | |
raw_id | integer | | plain | |
year | integer | | plain | |
company_id | integer | | plain | |
visa_class | character varying | | extended | |
employement_start_date | character varying | | extended | |
employement_end_date | character varying | | extended | |
employer_name | character varying | | extended | |
employer_address1 | character varying | | extended | |
employer_address2 | character varying | | extended | |
employer_city | character varying | | extended | |
employer_state | character varying | | extended | |
employer_postal_code | character varying | | extended | |
employer_phone | character varying | | extended | |
employer_phone_ext | character varying | | extended | |
job_title | character varying | | extended | |
soc_code | character varying | | extended | |
naic_code | character varying | | extended | |
prevailing_wage | character varying | | extended | |
pw_unit_of_pay | character varying | | extended | |
wage_unit_of_pay | character varying | | extended | |
worksite_city | character varying | | extended | |
worksite_state | character varying | | extended | |
worksite_postal_code | character varying | | extended | |
total_workers | integer | | plain | |
case_status | character varying | | extended | |
case_no | character varying | | extended | |
salary | real | | plain | |
salary_max | real | | plain | |
prevailing_wage_second | real | | plain | |
lawyer_id | integer | | plain | |
citizenship | character varying | | extended | |
class_of_admission | character varying | | extended | |
Indexes:
"lca_test_pkey" PRIMARY KEY, btree (id)
"index_lca_test_on_id_and_salary" btree (id, salary)
"index_lca_test_on_id_and_salary_and_year" btree (id, salary, year)
"index_lca_test_on_id_and_salary_and_year_and_wage_unit_of_pay" btree (id, salary, year, wage_unit_of_pay)
"index_lca_test_on_id_and_visa_class" btree (id, visa_class)
"index_lca_test_on_id_and_worksite_state" btree (id, worksite_state)
"index_lca_test_on_lawyer_id" btree (lawyer_id)
"index_lca_test_on_lawyer_id_and_company_id" btree (lawyer_id, company_id)
"index_lca_test_on_raw_id_and_visa_and_pw_second" btree (raw_id, visa_class, prevailing_wage_second)
"index_lca_test_on_raw_id_and_visa_class" btree (raw_id, visa_class)
"index_lca_test_on_salary" btree (salary)
"index_lca_test_on_visa_class" btree (visa_class)
"index_lca_test_on_wage_unit_of_pay" btree (wage_unit_of_pay)
"index_lca_test_on_worksite_state" btree (worksite_state)
"index_lca_test_on_year_and_company_id" btree (year, company_id)
"index_lca_test_on_year_and_company_id_and_case_status" btree (year, company_id, case_status)
"index_lcas_job_title_trigram" gin (job_title gin_trgm_ops)
"lca_test_company_id" btree (company_id)
"lca_test_employer_name" btree (employer_name)
"lca_test_id" btree (id)
"lca_test_on_year_and_companyid_and_wage_unit_and_salary" btree (year, company_id, wage_unit_of_pay, salary)
Foreign-key constraints:
"fk_Rails_8a90090fe0" FOREIGN KEY (lawyer_id) REFERENCES lawyers(id)
Has OIDs: no
similarity()
関数を提供する追加モジュール pg_trgm
をインストールしたことを忘れていました。
%
まず、他に何をする場合でも、式%
の代わりに、類似演算子(similarity(job_title, 'sales executive') > 0.6)
を使用します。もっと安い。そして、インデックスのサポートは関数ではなく、Postgresのoperatorsにバインドされています。
0.6
の望ましい最小類似度を取得するには、次のコマンドを実行します。
SELECT set_limit(0.6);
他の設定にリセットしない限り、設定はセッションの残りの部分に残ります。確認する:
SELECT show_limit();
これは少し不格好ですが、パフォーマンスには最適です。
文字列 'sales Executive'の列job_title
に最も一致したい場合、これは「最も近い隣人」検索の単純なケースであり、トライグラム演算子クラスGist_trgm_ops
を使用してGistインデックスで解決できます(ただし、GINは使用できません)インデックス):
CREATE INDEX trgm_idx ON lcas USING Gist (job_title Gist_trgm_ops);
worksite_city
に等価条件も含めるには、追加モジュール btree_Gist
が必要になります。実行(DBごとに1回):
CREATE EXTENSION btree_Gist;
次に:
CREATE INDEX lcas_trgm_Gist_idx ON lcas USING Gist (worksite_city, job_title Gist_trgm_ops);
クエリ:
SELECT set_limit(0.6); -- once per session
SELECT *
FROM lca_test
WHERE job_title % 'sales executive'
AND worksite_city = 'los angeles'
ORDER BY (job_title <-> 'sales executive')
LIMIT 50;
<->
「距離」演算子:
1マイナス
similarity()
値。
Postgresは、2つの個別のインデックス、worksite_city
のプレーンなbtreeインデックス、およびjob_title
の個別のGistインデックスを組み合わせることもできますが、クエリでこのように2つのカラムを定期的に組み合わせる場合、複数カラムのインデックスが最も高速になります。
ただし、クエリは距離/類似度ではなくsalary
で並べ替えられるため、ゲームの性質が完全に変わります。これでGINとGistインデックスの両方を使用できるようになり、GINはより高速になります(GINインデックスが大幅に改善されたPostgres 9.4ではさらにヒント-ヒント!)
worksite_city
の追加の等価チェックの同様のストーリー:追加のモジュールをインストールします btree_gin
。実行(DBごとに1回):
CREATE EXTENSION btree_gin;
次に:
CREATE INDEX lcas_trgm_gin_idx ON lcas USING gin (worksite_city, job_title gin_trgm_ops);
クエリ:
SELECT set_limit(0.6); -- once per session
SELECT *
FROM lca_test
WHERE job_title % 'sales executive'
AND worksite_city = 'los angeles'
ORDER BY salary
LIMIT 50 -- OFFSET 0
繰り返しになりますが、これは、すでに持っているより単純なインデックス("index_lcas_job_title_trigram"
)と(おそらく他のインデックスと組み合わせて)機能します(効率が悪くなります)。最善の解決策は全体像に依存します。
インデックスがたくさんあります。それらがすべて使用されていて、維持費を支払っていますか?
疑わしいデータタイプがいくつかあります。
employement_start_date | character varying
employement_end_date | character varying
それらはdate
である必要があるようです。等。
関連する回答: