約 750 万件のレコードを含むテーブルがあり、そのテーブルに基づいてオートコンプリート フォームを実装しようとしていますが、パフォーマンスはかなり悪いです。
スキーマ (無関係なフィールドは省略) は次のとおりです。
COMPANIES
---------
sid (integer primary key)
world_hq_sid (integer)
name (varchar(64))
marketing_alias (varchar(64))
address_country_code (char(4))
address_state (varchar(64))
sort_order integer
search_weight integer
annual_sales integer
渡されるフィールドは、オプションの国コードと州、および検索語です。私が望むのは、検索語が名前またはmarketing_aliasの先頭に一致することです(大文字と小文字は区別されません)。上位 10 件の結果が必要です。国と州にも一致する結果が一番上に表示され、次に国のみ、次に州/国に一致しない結果が表示されます。その後、結果を sort_order で並べ替えます。
また、world_hq_sid ごとに 1 つの一致のみが必要です。最後に、world_hq_sid ごとに上位の一致がある場合、最終結果を search_weight でソートしたいと考えています。
world_hq_sid 部分を実現するためにウィンドウ クエリを使用しています。クエリは次のとおりです。
SELECT * FROM (
SELECT ROW_NUMBER() OVER (PARTITION BY world_hq_sid ORDER BY CASE WHEN address_country_code = 'US' AND address_state = 'CA' THEN 2 WHEN address_country_code = 'US' THEN 1 ELSE 0 END desc, sort_order asc) AS r,
companies.*
FROM companies
WHERE ((upper(name) LIKE upper('co%')) OR (upper(marketing_alias) LIKE upper('co%')))
) x
WHERE x.r = 1
ORDER BY CASE WHEN address_country_code = 'US' AND address_state = 'CA' THEN 2 WHEN address_state = 'CA' THEN 1 ELSE 0 END desc, search_weight asc, annual_sales desc
LIMIT 10;
address_state、address_country_code、world_hq_sid、sort_order、および search_weight に通常の btree インデックスがあります。
name フィールドと marketing_alias フィールドに次のインデックスがあります。
CREATE INDEX companies_alias_pattern_upper_idx ON companies(upper(marketing_alias) varchar_pattern_ops);
CREATE INDEX companies_name_pattern_upper_idx ON companies(upper(name) varchar_pattern_ops)
CA を状態として、「co」を検索語として渡した場合の説明分析は次のとおりです。
Limit (cost=676523.01..676523.03 rows=10 width=939) (actual time=18695.686..18695.687 rows=10 loops=1)
-> Sort (cost=676523.01..676526.67 rows=1466 width=939) (actual time=18695.686..18695.687 rows=10 loops=1)
Sort Key: x.search_weight, x.annual_sales
Sort Method: top-N heapsort Memory: 30kB
-> Subquery Scan on x (cost=665492.58..676491.33 rows=1466 width=939) (actual time=18344.715..18546.830 rows=151527 loops=1)
Filter: (x.r = 1)
Rows Removed by Filter: 20672
-> WindowAgg (cost=665492.58..672825.08 rows=293300 width=931) (actual time=18344.710..18511.625 rows=172199 loops=1)
-> Sort (cost=665492.58..666225.83 rows=293300 width=931) (actual time=18344.702..18359.145 rows=172199 loops=1)
Sort Key: companies.world_hq_sid, (CASE WHEN ((companies.address_state)::text = 'CA'::text) THEN 1 ELSE 0 END), companies.sort_order
Sort Method: quicksort Memory: 108613kB
-> Bitmap Heap Scan on companies (cost=17236.64..518555.98 rows=293300 width=931) (actual time=1861.665..17999.806 rows=172199 loops=1)
Recheck Cond: ((upper((name)::text) ~~ 'CO%'::text) OR (upper((marketing_alias)::text) ~~ 'CO%'::text))
Filter: ((upper((name)::text) ~~ 'CO%'::text) OR (upper((marketing_alias)::text) ~~ 'CO%'::text))
-> BitmapOr (cost=17236.64..17236.64 rows=196219 width=0) (actual time=1829.061..1829.061 rows=0 loops=1)
-> Bitmap Index Scan on companies_name_pattern_upper_idx (cost=0.00..8987.98 rows=97772 width=0) (actual time=971.331..971.331 rows=169390 loops=1)
Index Cond: ((upper((name)::text) ~>=~ 'CO'::text) AND (upper((name)::text) ~<~ 'CP'::text))
-> Bitmap Index Scan on companies_alias_pattern_upper_idx (cost=0.00..8102.02 rows=98447 width=0) (actual time=857.728..857.728 rows=170616 loops=1)
Index Cond: ((upper((marketing_alias)::text) ~>=~ 'CO'::text) AND (upper((marketing_alias)::text) ~<~ 'CP'::text))
work_mem と shared_buffers を 100M に増やしました。
ご覧のとおり、このクエリは 18 秒で返されます。奇妙なのは、400 ミリ秒 (許容範囲) から 30 秒 (非常に許容範囲外) まで、さまざまな開始文字の結果が全面的に出ていることです。Postgres の教祖、私の質問は、postgresql があまりにも多くのことを期待して、そのようなクエリを一貫して迅速に実行できないのでしょうか? これをスピードアップする方法はありますか?