@Satyaがコメントで主張していることは、まったく真実ではありません。一致するインデックスが存在する場合、プランナーは、テーブル統計がテーブルの約 5% (依存) よりも多くを返すことを示唆している場合にのみ、フル テーブル スキャンを選択します。これは、テーブル全体をスキャンする方が高速であるためです。
あなた自身の質問からわかるように、これはあなたのクエリには当てはまりません。Bitmap Index Scanに続いてBitmap Heap Scanを使用します。単純なインデックススキャンを期待していましたが。(?)
Explain の出力でさらに 2 つのことに気付きました
。最初のスキャンでは 832 行が検出されましたが、2 番目のスキャンではカウントが 739 に減少しました。これは、インデックスに多くのデッド タプルがあることを示しています。
各ステップの実行時間を確認しEXPLAIN ANALYZE
、結果を質問に追加してください。
まず、EXPLAIN ANALYZE を使用してクエリを 2 ~ 3 回再実行し、キャッシュにデータを取り込みます。最初の実行と比較した最後の実行の結果は?
次:
VACUUM ANALYZE cars;
再実行します。
テーブルに多くの書き込み操作がある場合は、フィル ファクターを 100 未満に設定します。
ALTER TABLE cars SET (fillfactor=90);
行サイズが大きい場合や書き込み操作が多い場合は、値を下げてください。それで:
VACUUM FULL ANALYZE cars;
これにはしばらく時間がかかります。再実行します。
または、これを行う余裕がある場合 (および他の重要なクエリに矛盾する要件がない場合):
CLUSTER cars USING index_cars_on_reference_id;
これにより、インデックスの物理的な順序でテーブルが書き換えられるため、この種のクエリが大幅に高速化されます。
スキーマを正規化する
これを非常に高速にする必要がある場合は、主キーを持つテーブルcar_type
をserial
作成し、テーブルから参照しますcars
。これにより、必要なインデックスが現在の数分の一に縮小されます。
言うまでもなく、これを試す前にバックアップを作成してください。
CREATE temp TABLE car_type (
car_type_id serial PRIMARY KEY
, car_type text
);
INSERT INTO car_type (car_type)
SELECT DISTINCT car_type_id FROM cars ORDER BY car_type_id;
ANALYZE car_type;
CREATE UNIQUE INDEX car_type_uni_idx ON car_type (car_type); -- unique types
ALTER TABLE cars RENAME COLUMN car_type_id TO car_type; -- rename old col
ALTER TABLE cars ADD COLUMN car_type_id int; -- add new int col
UPDATE cars c
SET car_type_id = ct.car_type_id
FROM car_type ct
WHERE ct.car_type = c.car_type;
ALTER TABLE cars DROP COLUMN car_type; -- drop old varchar col
CREATE INDEX cars_car_type_id_idx ON cars (car_type_id);
ALTER TABLE cars
ADD CONSTRAINT cars_car_type_id_fkey FOREIGN KEY (car_type_id )
REFERENCES car_type (car_type_id) ON UPDATE CASCADE; -- add fk
VACUUM FULL ANALYZE cars;
または、全力を出したい場合は、次のようにします。
CLUSTER cars USING cars_car_type_id_idx;
クエリは次のようになります。
SELECT count(*)
FROM cars
WHERE car_type_id = (SELECT car_type_id FROM car_type
WHERE car_type = 'toyota_hilux')
そして、さらに速くなるはずです。主にインデックスとテーブルが小さくなったためですが、integer
処理が処理よりも高速であるためvarchar
です。varchar
ただし、列のクラスター化されたテーブルに対して劇的な効果は得られません。
歓迎すべき副作用: 型の名前を変更する必要がある場合UPDATE
、大きなテーブルをまったくいじることなく、小さな行から 1 つの行になります。