A covering index is an index that contains all the columns a query needs—so the database can satisfy the query entirely from the index, without touching the underlying table (also known as avoiding a table (or heap) lookup).
🧠 Definition
A covering index “covers” a query when all columns used in the SELECT, WHERE, JOIN, or ORDER BY clauses are included in the index.
✅ Why It’s Fast
- Fewer I/O operations: No need to fetch the full row from the table.
- Especially powerful for read-heavy workloads and OLAP-style queries.
📌 Example (MySQL / PostgreSQL)
Table:
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(100),
email VARCHAR(100),
status VARCHAR(20)
);
Query:
SELECT email FROM users WHERE status = 'active';
Covering Index:
CREATE INDEX idx_status_email ON users(status, email);
status
→ used inWHERE
email
→ used inSELECT
- ✅ The index covers the query → no need to fetch from table
🔎 MySQL Note (InnoDB)
In MySQL, the primary key is always implicitly included in every secondary index (since it’s needed to locate the row), so you don’t need to add it explicitly unless your query needs it directly.
📌 PostgreSQL Note
PostgreSQL uses the term “Index Only Scan” when a covering index is used. But it requires visibility map bits to confirm that the row is visible (i.e., not recently updated), so sometimes it still touches the table.
Use:
EXPLAIN ANALYZE SELECT email FROM users WHERE status = 'active';
And look for:
Index Only Scan using idx_status_email
🧪 Test If You Need It
- Run
EXPLAIN
orEXPLAIN ANALYZE
and check if a table/heap fetch occurs - If yes, a covering index could eliminate that extra step
📎 Recap
Feature | Covering Index ✅ |
---|---|
All columns in index | ✅ |
Avoids table lookup | ✅ |
Improves performance | ✅ Especially for reads |
Maintained on writes | ⚠️ Slower inserts/updates |