Advanced indexes

Draft

After the first index, the next lesson is shape. Useful indexes depend on how selective the filter is and how the query is written.

Low selectivity

An index on status exists, but paid matches most rows, so a scan can still be cheaper.

SELECT COUNT(*) AS paid_orders
FROM orders
WHERE status = 'paid';
paid_orders
10800
Aggregate  (cost=277.00..277.01 rows=1 width=8)
  ->  Seq Scan on orders  (cost=0.00..250.00 rows=10800 width=0)
        Filter: (status = 'paid'::text)

Composite index

This query matches the status, created_at index shape: filter first, then order.

SELECT order_number, status, created_at
FROM orders
WHERE status = 'pending'
ORDER BY created_at DESC
LIMIT 5;
order_numberstatuscreated_at
ORD-11990pending2026-01-09T07:50:00.000Z
ORD-11980pending2026-01-09T07:40:00.000Z
ORD-11970pending2026-01-09T07:30:00.000Z
ORD-11960pending2026-01-09T07:20:00.000Z
ORD-11950pending2026-01-09T07:10:00.000Z
Limit  (cost=0.29..1.70 rows=5 width=22)
  ->  Index Scan using orders_status_created_at_idx on orders  (cost=0.29..305.54 rows=1080 width=22)
        Index Cond: (status = 'pending'::text)

Partial index

A partial index stores only failed orders, which keeps a rare lookup small.

SELECT order_number, status, total_cents
FROM orders
WHERE status = 'failed'
  AND order_number = 'ORD-9000';
order_numberstatustotal_cents
ORD-9000failed10000
Index Scan using orders_failed_order_number_idx on orders  (cost=0.14..8.16 rows=1 width=18)
  Index Cond: (order_number = 'ORD-9000'::text)

Try Yourself

Loaded Database:advanced-indexes.database-init

The database for this lesson is already loaded. Write any query you want and run it directly in your browser.

Exercises

0 OF 2 DONE
1.Select the 3 newest paid orders using status, ORDER BY created_at DESC, and LIMIT.
2.Select failed order ORD-12000 using both status and order_number.

What We Learned