SQL: an index on a low-cardinality column is usually ignored

If a value matches a large fraction of the table, reading the index and then fetching those rows costs more than scanning. The planner knows this and skips the index you added.

Code
CREATE INDEX idx_orders_status ON orders (status);   -- 3 distinct values

SELECT * FROM orders WHERE status = 'PAID';       -- 73% of rows -> seq scan
SELECT * FROM orders WHERE status = 'REFUND';     -- 1% of rows  -> index scan

-- A partial index only covers the selective value, and is far smaller:
CREATE INDEX idx_orders_refund ON orders (created_at)
  WHERE status = 'REFUND';
Output
status='PAID'   -> Seq Scan   (rows=7,210 of 9,876)
status='REFUND' -> Index Scan (rows=118)

-- The same index, two plans, both correct. "The index is not being used"
-- is usually the planner being right.
--
-- The partial index above is 118 entries instead of 9,876.
Advertisement

Run this yourself in the Online Java Compiler, spin up a live REST API in the API Sandbox, or practise with Java interview questions.

Published 2026-08-25