R005 · default CAUTION

Missing Foreign Key Index

Postgres does not auto-create an index on the child side of a foreign key. Without one, JOINs and DELETEs on the parent are sequential scans.

Why this is a problem

This is the #1 silent performance issue in Postgres apps. Missing FK index → DELETE on parent triggers seq scan on child to enforce referential integrity. On a 240M-row child, every parent DELETE is a 47-second query.

Don't do this

-- FK without backing index — slow joins, slow parent deletes
ALTER TABLE orders
  ADD CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES users(id);

Do this instead

-- Add the index FIRST (concurrently to avoid lock)
CREATE INDEX CONCURRENTLY orders_user_id_idx ON orders (user_id);

-- Then add the FK
ALTER TABLE orders
  ADD CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES users(id) NOT VALID;
ALTER TABLE orders VALIDATE CONSTRAINT fk_user;

References

Catch this rule on every PR.

dbivio runs all 10 rules automatically with your real Postgres stats. Install once, get a verdict on every migration PR.

Install on GitHub