複合インデックス
2列で絞るクエリ
実務でよく流れるのは、1列だけでなく複数の条件で絞るクエリです。
SELECT * FROM orders
WHERE store_id = 3 AND ordered_on = DATE '2026-03-01';このとき、store_id の索引と ordered_on の索引を別々に作るより、2列をまとめた1本の索引のほうがよく効きます。これを複合インデックスと呼びます。
CREATE INDEX orders_store_ordered_idx ON orders (store_id, ordered_on);列の順番が意味を持つ
複合インデックスで最も重要なのが、列を書いた順番です。
電話帳を思い浮かべると分かります。姓、名の順に並んだ電話帳では、次のことができます。
| 探し方 | できるか |
|---|---|
| 姓と名の両方が分かっている | できる |
| 姓だけ分かっている | できる。その姓の範囲に絞れる |
| 名だけ分かっている | できない。全部めくることになる |
索引もまったく同じです。(store_id, ordered_on) の索引は、store_id だけの条件でも使えますが、ordered_on だけの条件では絞り込めません。
この性質を左端優先と呼びます。左から順に条件がそろっているぶんだけ使える、という意味です。
順番の決め方
どちらを左に置くかは、次の順で考えます。
1. 等号で絞る列を左に。 = 3 のように1点に決まる条件は、範囲条件より先に置きます。範囲条件を左に置くと、そこから右の列は絞り込みに使えなくなります。
2. 単独でも使う列を左に。 左端の列は単独の索引としても働くので、store_id だけで絞るクエリがあるなら store_id を左にします。
索引を1本減らせる
左端優先には嬉しい副作用があります。(store_id, ordered_on) があれば、store_id 単独の索引は要りません。複合インデックスが単独の索引を兼ねるからです。
索引は少ないほど書き込みが軽くなるので、兼ねられるものは兼ねさせます。設計表を作るときは、この重なりを必ず見ます。
手を動かす
複合インデックスを作り、条件の組み合わせを変えて計画がどう変わるかを見ます。
テーブル構造
CREATE TABLE orders (
id integer PRIMARY KEY,
store_id integer NOT NULL,
customer_id integer NOT NULL,
ordered_on date NOT NULL,
status text NOT NULL,
total integer NOT NULL
);
INSERT INTO orders
SELECT g,
1 + (g % 5),
1 + (g % 20000),
DATE '2026-01-01' + (g % 366),
(ARRAY['paid', 'shipped', 'cancelled'])[1 + (g % 3)],
1000 + (g % 50000)
FROM generate_series(1, 100000) AS g;
ANALYZE orders;
期待される出力
| has_index | matched |
|---|---|
| 1 | 55 |