A Simple Guide to SQL Indexes: What They Are, Why They Matter, and When to Use Them

If you've ever had a query drag on forever and thought there has to be a faster way, you're absolutely right. Indexes are one of the easiest and most effective ways to speed up data retrieval in SQL.

In this post, we will go through:

  • What indexes are
  • Why we use them
  • Different types of indexes
  • Rowstore vs columnstore
  • When to use which type

What Is an Index?

An index in SQL works much like the index in a textbook, it helps the system find what you are looking for quickly without reading every row. Without indexes, the database must scan the whole table every time you run a query with a filter.

Indexes are created on one or more columns and allow the engine to look things up much faster. However, they come with a cost, so it's important to understand when and how to use them.


βš™οΈ Why Use Indexes?

Advantages:

  • Speeds up SELECT queries significantly
  • Helps with sorting using ORDER BY
  • Improves performance on JOIN and GROUP BY

Things to watch out for:

  • Slower write operations like INSERT, UPDATE, DELETE as the index has to be updated as well
  • Extra storage usage

Use indexes to improve reads, but be careful if your database is write-heavy.


Common Types of Indexes

1. Single-column index

Use this when you are filtering or sorting using just one column.

Syntax:

CREATE INDEX 'index_name'
ON 'table_name' ('column_name');

Use case:

SELECT * FROM 'table_name' WHERE 'column_name' = 'value';

2. Composite index (multi-column)

Use this when you often filter or sort using two or more columns together.

Syntax:

CREATE INDEX 'index_name'
ON 'table_name' ('first_column', 'second_column');

The order of the columns matters. This index helps:

SELECT * FROM 'table_name' WHERE 'first_column' = X AND 'second_column' = Y;

But not:

SELECT * FROM 'table_name' WHERE 'second_column' = Y;

3. Unique index

This prevents duplicate values in one or more columns.

Syntax:

CREATE UNIQUE INDEX 'index_name'
ON 'table_name' ('column_name');

Used for things like usernames or email addresses where duplicates should not exist.


4. Primary key index

Automatically created when you define a primary key on a table. It ensures values are unique and not null.

Example:

CREATE TABLE 'table_name' (
    'column_name' INT PRIMARY KEY
);

You do not need to create this one manually β€” the database will do it for you.


5. Clustered index

A clustered index defines the physical order of rows on disk. Most databases only allow one clustered index per table.

Syntax:

CREATE CLUSTERED INDEX 'index_name'
ON 'table_name' ('column_name');

Best for:

  • Sorting
  • Range queries
  • Queries like:
SELECT * FROM 'table_name' WHERE 'column_name' BETWEEN A AND B ORDER BY 'column_name';

6. Non-clustered index

This is a separate structure that points to the actual rows. You can have several non-clustered indexes per table.

Syntax:

CREATE NONCLUSTERED INDEX 'index_name'
ON 'table_name' ('column_name');

Good for:

  • Lookups and filtering
  • Exact match queries

🧱 Rowstore vs πŸ“Š Columnstore Indexes

These terms describe how data is stored and retrieved β€” and they have very different use cases.


Rowstore index

This is the default. Data is stored and indexed row by row.

Use rowstore indexes for:

  • Transactional systems
  • Systems with lots of inserts and updates
  • Traditional queries like:
SELECT * FROM 'table_name' WHERE 'column_name' = 'value';

Columnstore index

Data is stored column by column rather than row by row. It is highly compressed and optimised for aggregation.

Syntax (SQL Server):

CREATE CLUSTERED COLUMNSTORE INDEX 'index_name'
ON 'table_name';

Use columnstore indexes for:

  • Data warehousing
  • Dashboards
  • Analytics like:
SELECT 'region', AVG('amount')
FROM 'table_name'
GROUP BY 'region';

Columnstore indexes are not ideal for frequent updates or point lookups β€” they shine in large-scale reporting.


What is INCLUDE in an Index?

Sometimes you filter using one set of columns, but also want to retrieve other columns in the result. That is where INCLUDE comes in.

Here’s a common problem:

SELECT 'customer_id'
FROM 'table_name'
WHERE 'country' = 'UK' AND 'score' = 200;

You might think an index on ('country', 'score') would be perfect here. But if the index does not contain 'customer_id', the database still has to go back to the main table to fetch it.

If you want the index to cover the query fully, you can include 'customer_id' in the index.

Syntax:

CREATE NONCLUSTERED INDEX 'index_name'
ON 'table_name' ('country', 'score')
INCLUDE ('customer_id');

Use INCLUDE when:

  • The columns in the SELECT list are not part of the filtering but still need to be returned
  • You want to avoid the extra step of looking up the base table

To Summarize: When to Use Which Index

Single-column index
When your queries filter or sort by one specific column

Composite index
When you regularly filter or sort by the same combination of two or more columns

Unique index
When you need to enforce uniqueness (such as usernames or email addresses)

Primary key index
Always β€” this ensures each row has a unique identifier

Clustered index
When you frequently sort or filter by a column range, like dates or prices

Non-clustered index
When you need to speed up lookups on other columns not in the primary key

Rowstore index
When you are building transactional applications with frequent inserts and updates

Columnstore index
When building reporting or analytical queries over large volumes of data

Author:
Aronas Zilys
Powered by The Information Lab
1st Floor, 25 Watling Street, London, EC4M 9BR
Subscribe
to our Newsletter
Get the lastest news about The Data School and application tips
Subscribe now
Β© 2025 The Information Lab