An index is a data structure used to speed up the retrieval of records from a database table. Think of it like an index at the back of a book.
Types of Indexes
- Clustered Index: Defines the physical order in which data is stored in the table. A table can have only one.
- Non-Clustered Index: Stores a separate list of values with a pointer to the actual data row. Multiple can exist per table.
- Unique Index: Ensures that no two rows have the same value in the indexed column.
- Composite Index: An index on multiple columns.
Clustered Index
The clustered index is the table. Data pages are sorted and stored according to the index key. When you create a PRIMARY KEY, most databases automatically create a clustered index on it.
-- SQL Server: Create clustered index explicitly
CREATE CLUSTERED INDEX IX_Orders_Date ON Orders(order_date);
Only one clustered index per table because data can only be physically sorted one way.
Non-Clustered Index
A non-clustered index is a separate structure containing the indexed columns plus a pointer (row locator) to the actual data row. It is like the index at the back of a textbook — you look up the topic and get a page number.
CREATE NONCLUSTERED INDEX IX_Employees_Name ON Employees(name);
Composite (Multi-Column) Index
An index on multiple columns. The leftmost prefix rule applies: the index can be used for queries that filter on the first column, first + second, or all columns.
CREATE INDEX IX_Orders_UserDate ON Orders(user_id, order_date);
This index can optimise:
WHERE user_id = ?(uses first column)WHERE user_id = ? AND order_date = ?(uses both)- But NOT
WHERE order_date = ?alone (skips the first column)
Covering Index
A covering index includes all columns needed by a query (both SELECT and WHERE). The database can satisfy the query entirely from the index without touching the main table. This is the fastest type of index access.
-- If the query is:
SELECT name, salary FROM employees WHERE department_id = 5;
-- A covering index would be:
CREATE INDEX IX_Covering ON employees(department_id) INCLUDE (name, salary);
Index Data Structures
| Type | Best For |
|---|---|
| B-Tree | High-cardinality data (IDs, names, emails) — range queries, sorting |
| Hash | Exact match lookups (WHERE id = ?) |
| Bitmap | Low-cardinality data (gender, status) — data warehouses |
| GiST/GIN | Full-text search, arrays, JSON (PostgreSQL) |
When to Add an Index
- Columns used frequently in
WHERE,JOIN, orORDER BY. - Columns with high selectivity (many unique values).
- Foreign key columns.
When to Avoid Indexes
- Small tables (full scan is cheaper).
- Columns rarely used in queries.
- Columns with very low selectivity (e.g., boolean flags — unless combined with other columns).
Q: Difference between Clustered and Non-Clustered Index?
A:
Clustered:The index is the table. Data is stored physically in the order of the index. (Example: Dictionary).Non-Clustered:The index is separate from the data. It contains the key and a pointer to the data row. (Example: Index at the back of a textbook).
Q: What is a Covering Index?
A: A covering index is a non-clustered index that includes all columns required by a query (in both SELECT and WHERE). If an index “covers” a query, the DB doesn’t even need to look at the main table, making it extremely fast.
Q: When does an index slow down performance?
A: Indexes slow down DML operations (INSERT, UPDATE, DELETE) because the database engine must update both the table and all associated indexes.
Q: What is the leftmost prefix rule?
A: For a composite index on (A, B, C), the index can be used for queries on A, A+B, or A+B+C. It cannot be used for queries on B or C alone because those aren’t leftmost prefixes.
1. Choose index for: SELECT * FROM orders WHERE user_id = ? ORDER BY created_at DESC.
Answer: A composite index on (user_id, created_at) is ideal. The DB can use the first part to filter and the second part to sort without a separate “Sort” operation.
2. Decide Composite Index Order: (last_name, first_name) or (first_name, last_name).
Answer: Use the column with higher cardinality (most unique values) first, or the one most frequently used in WHERE clauses alone. Usually, last_name is better if searching by surname is more common.
3. Compare B-Tree vs Bitmap Index.
Answer:
- B-Tree: Best for high-cardinality data (IDs, Names, Emails). Most common.
- Bitmap: Best for low-cardinality data (Gender, Boolean, Status) and heavily used in Data Warehousing.
4. How would you index a table with 1M rows queried by status (3 values) and created_at?
Answer: A composite index on (status, created_at) works well. Status filters to a subset, then created_at handles sorting and range filtering. Since status has low cardinality, adding created_at improves selectivity.
Premium Content
Unlock Indexes Deep Dive and all premium lessons with a subscription.
From ₹199.99/year — See plans