Medium-High Impact

Use Covering Indexes to Avoid Table Lookups

2-5x faster queries by eliminating heap fetches

Why This Matters

Covering indexes include all columns needed by a query, enabling index-only scans that skip the table entirely. Without them, Postgres must perform an extra "heap fetch" to get columns not in the index.

This is especially valuable when:

The Problem

Incorrect - Index scan + heap fetch
CREATE INDEX users_email_idx ON users (email);

-- Must fetch name and created_at from table heap
SELECT email, name, created_at 
FROM users 
WHERE email = '[email protected]';

The Solution

Correct - Index-only scan with INCLUDE
-- Include non-searchable columns in the index
CREATE INDEX users_email_idx ON users (email) 
INCLUDE (name, created_at);

-- All columns served from index, no table access needed
SELECT email, name, created_at 
FROM users 
WHERE email = '[email protected]';

INCLUDE vs adding to index: INCLUDE columns are stored in the index but not used for searching. This keeps the index smaller and faster than adding them as regular index columns.

Another common pattern - including columns you SELECT but don't filter on:

-- Searching by status, but also need customer_id and total
CREATE INDEX orders_status_idx ON orders (status) 
INCLUDE (customer_id, total);

SELECT status, customer_id, total 
FROM orders 
WHERE status = 'shipped';

Check Your Project Automatically

Connect your Supabase project to identify opportunities for covering indexes.

Start Supabase Audit

Common Mistakes

Learn More