Menu

Earn Premium with Referrals

Invite your friends and earn Premium rewards through our referral program.

See how it works and start inviting friends.

Indexes Deep Dive
SQL

Indexes Deep Dive

Explore the different types of indexes and how to choose the right one for your data access patterns.

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

  1. Clustered Index: Defines the physical order in which data is stored in the table. A table can have only one.
  2. Non-Clustered Index: Stores a separate list of values with a pointer to the actual data row. Multiple can exist per table.
  3. Unique Index: Ensures that no two rows have the same value in the indexed column.
  4. 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

TypeBest For
B-TreeHigh-cardinality data (IDs, names, emails) — range queries, sorting
HashExact match lookups (WHERE id = ?)
BitmapLow-cardinality data (gender, status) — data warehouses
GiST/GINFull-text search, arrays, JSON (PostgreSQL)

When to Add an Index

  • Columns used frequently in WHERE, JOIN, or ORDER 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.

My Private Notes

Notes are auto-saved locally to this device.