Indexes Basics
9 examples to get you started with PostgreSQL indexes - 6 basic and 3 intermediate.
Search across all documentation pages
9 examples to get you started with PostgreSQL indexes - 6 basic and 3 intermediate.
Table with thousands of rows for meaningful EXPLAIN output.
# Local dev with Docker (PostgreSQL 18)
docker run -d --name pg18 -e POSTGRES_PASSWORD=dev -p 5432:5432 postgres:18
psql postgres://postgres:dev@localhost:5432/postgresDefault access method for equality and range.
CREATE INDEX customers_email_idx ON app.customers (email);
EXPLAIN SELECT * FROM app.customers WHERE email = 'ada@example.com';Leading column must match predicate.
CREATE INDEX orders_account_created_idx ON app.orders (account_id, created_at DESC);Every index updates on row change.
INSERT INTO app.customers (email) VALUES ('new@example.com');
-- updates customers_email_idx plus PKEnforces uniqueness and accelerates lookup.
CREATE UNIQUE INDEX customers_email_unique ON app.customers (lower(email));Catalog inspection.
SELECT indexname, indexdef
FROM pg_indexes
WHERE schemaname = 'app' AND tablename = 'customers';Small tables or wide fraction of rows.
EXPLAIN ANALYZE SELECT * FROM app.customers;Avoid long write locks in production.
CREATE INDEX CONCURRENTLY customers_created_idx
ON app.customers (created_at);Index-only friendly projections.
CREATE INDEX orders_cover_idx ON app.orders (account_id) INCLUDE (total);pg_stat_user_indexes.
SELECT relname, indexrelname, idx_scan, idx_tup_read
FROM pg_stat_user_indexes
WHERE schemaname = 'app'
ORDER BY idx_scan;Stack versions: This page was written for PostgreSQL 18.4 (stable 18, maintenance 17), pgvector 0.8+, PgBouncer 1.x, Patroni 3.x, and PostGIS 3.5+.
Reviewed by Chris St. John·Last updated Jul 16, 2026