テック技報最新号へ →
コード

Postgres: 外部キーの列には自動でインデックスが付かない。子テーブル側に自分で張る

REFERENCES を書いてもインデックスが付くのは参照される側(主キー・ユニーク)だけで、参照する側の列には付かない。親の削除や更新、JOIN のたびに子テーブルを全件走査することになるので、FK 列にインデックスを張る。

@amitsinha__07 · 投稿 2026-10-22
#postgres#infra#index#performance

何が遅くなるか

sql
CREATE TABLE orders (
  id      bigint PRIMARY KEY,
  user_id bigint NOT NULL REFERENCES users(id)  -- ここにはインデックスがない
);
  • DELETE FROM users WHERE id = 1 のとき、Postgres は「この user を参照する orders がないか」を確かめるため orders を走査する。ON DELETE CASCADE でも同じ。
  • WHERE user_id = $1 や JOIN orders ON orders.user_id = users.id も遅い。
  • 親の削除中は子テーブル側のチェックに時間がかかり、その分ロックも長く持つ。

対処

sql
CREATE INDEX CONCURRENTLY orders_user_id_idx ON orders (user_id);

インデックスのない FK 列を探すクエリ(単一列 FK の簡易版):

sql
SELECT c.conrelid::regclass AS table_name,
       a.attname            AS fk_column
FROM pg_constraint c
JOIN pg_attribute a ON a.attrelid = c.conrelid AND a.attnum = c.conkey[1]
WHERE c.contype = 'f'
  AND array_length(c.conkey, 1) = 1
  AND NOT EXISTS (
    SELECT 1 FROM pg_index i
    WHERE i.indrelid = c.conrelid AND i.indkey[0] = c.conkey[1]
  );

注意点

出典

X の元の投稿を開く