Working with Indexes

1. Creating Index

CREATE INDEX orders_customer_id_idx ON orders(customer_id);
CREATE INDEX orders_placed_at_idx   ON orders(placed_at DESC);

2. Creating Unique Index

CREATE UNIQUE INDEX customers_email_lower_uidx
   ON customers (lower(email));

3. Creating Partial Index

CREATE INDEX orders_open_idx ON orders(placed_at)
 WHERE status IN ('new','paid');
BenefitDetail
SmallerIndexes only matching rows
Faster lookupsPredicate hot-path
MatchingQuery predicate must imply index predicate

4. Creating Expression Index

CREATE INDEX events_user_id_idx ON events ((payload->>'user_id'));
CREATE INDEX customers_email_lc ON customers ((lower(email)));

5. Creating Multicolumn Index

CREATE INDEX orders_cust_placed_idx ON orders(customer_id, placed_at DESC);
Note: Leftmost column is mandatory for the index to be used; predicate on second column alone bypasses it.

6. Using B-tree Index

OperatorSupported
= < <= >= >Yes
BETWEEN, INYes
ORDER BYYes (ASC/DESC)
LIKE 'prefix%'Yes with text_pattern_ops

7. Using Hash Index

CREATE INDEX events_session_hash ON events USING hash (session_id);
PropertyDetail
Operator= only
WAL-loggedYes (since PG 10)
Use caseLarge equality-only lookup on long keys

8. Using GiST Index

CREATE INDEX reservations_during_gist ON reservations USING gist (during);
CREATE INDEX shops_loc_gist ON shops USING gist (location);
Data TypesOperators
range, geometric, tsvector@>, &&, <>
PostGIS geometrySpatial predicates

9. Using GIN Index

Data TypesOperators
arrays, jsonb, tsvector, hstore@>, ?, &&, @@
trigram (pg_trgm)LIKE/ILIKE, %, <%

10. Using BRIN Index

CREATE INDEX events_created_brin ON events USING brin (created_at) WITH (pages_per_range = 32);
PropertyDetail
Best forAppend-only, time-ordered fact tables
SizeTiny (KB for GB tables)
PerformanceRange scans only; not point lookups

11. Using SP-GiST Index

CREATE INDEX url_spgist ON urls USING spgist (url text_ops);
Use CaseData
Phone numbersPrefix-trie
IP rangesk-d tree
Quadtrees / point cloudsSpatial

12. Creating Covering Index

CREATE INDEX orders_cust_covering
   ON orders(customer_id) INCLUDE (status, total_cents);
Note: INCLUDE columns aren't in the search key but enable index-only scans for additional projected columns.

13. Creating Index Concurrently

CREATE INDEX CONCURRENTLY orders_cust_idx ON orders(customer_id);
PropertyDetail
LockShareUpdateExclusive (writes proceed)
CostTwo scans; slower wall clock
FailureLeaves invalid index; REINDEX or drop & retry

14. Listing Indexes

\di+
SELECT indexname, indexdef FROM pg_indexes
 WHERE schemaname='public' AND tablename='orders';

15. Dropping Index

DROP INDEX IF EXISTS orders_cust_idx;
DROP INDEX CONCURRENTLY orders_cust_idx;