Skip to main content

Composite Index

Editorial

Introduction

Most real-world queries filter on more than one column. When your WHERE clause or JOIN condition involves multiple columns, a single-column index may not be enough. A composite index — an index on two or more columns — is the answer.

In this tutorial, you will learn:

  • What a composite index is
  • How column order affects query performance
  • The "leftmost prefix" rule
  • When to use composite indexes vs multiple single-column indexes
  • How to design effective composite indexes

What is a Composite Index?

A composite index (also called a multi-column index or compound index) is an index that includes two or more columns as its key columns.

-- A composite index on two columns
CREATE NONCLUSTERED INDEX IX_Orders_Customer_Status
ON orders (customer_id, order_status);

The data in this index is sorted first by customer_id, and then within each customer, it is sorted by order_status.

Generated visual representation from text diagram for What is a Composite Index?.

The Leftmost Prefix Rule

The most important concept for composite indexes is the leftmost prefix rule. A composite index can only be used efficiently if the query filters on the leftmost (first) column of the index.

Given Index: (customer_id, order_status, order_date)

Query FilterUses This Index?Why
WHERE customer_id = 'X'✅ YesMatches first column
WHERE customer_id = 'X' AND order_status = 'Y'✅ YesMatches first two columns
WHERE customer_id = 'X' AND order_status = 'Y' AND order_date = 'Z'✅ YesMatches all three columns
WHERE order_status = 'Y'❌ No (or limited)Skips first column
WHERE order_date = 'Z'❌ NoSkips first two columns
WHERE order_status = 'Y' AND order_date = 'Z'❌ NoSkips first column
WHERE customer_id = 'X' AND order_date = 'Z'⚠️ PartialUses customer_id, skips status

Phone Book Analogy

Diagram explaining the leftmost prefix rule for composite indexes with examples of which queries can and cannot use the index

Column Order Matters

The order of columns in a composite index is a critical design decision. Here is how to choose:

Guidelines for Column Order

PositionChoose Column That...Reason
1st (leftmost)Is used in the most queriesMaximizes index reuse
2ndFurther narrows the search after the first columnImproves selectivity
3rd+Used in ORDER BY or further filteringAdditional refinement

Example: Two Different Orders

-- Option A: customer_id first
CREATE INDEX IX_A ON orders (customer_id, order_status);

-- Option B: order_status first
CREATE INDEX IX_B ON orders (order_status, customer_id);
QueryIX_A (cust, status)IX_B (status, cust)
WHERE customer_id = 'X'✅ Used❌ Not used
WHERE order_status = 'Y'❌ Not used✅ Used
WHERE customer_id = 'X' AND order_status = 'Y'✅ Used✅ Used

If most queries filter by customer_id, Option A is better. If most queries filter by order_status, Option B is better.

Selectivity Rule

Another consideration: place the most selective column first (the column with the most unique values).

ColumnUnique ValuesSelectivity
customer_id~99,000Very high
order_status~8Very low

Putting customer_id first is generally better because it narrows down the search to a small set of rows immediately.

Composite Index vs Multiple Single-Column Indexes

A common question: should I create one composite index or multiple single-column indexes?

Generated visual representation from text diagram for Composite Index vs Multiple Single-Column Indexes.

Recommendation

SituationBest Choice
Queries always filter on both columns togetherComposite index
Queries sometimes filter on only one columnComposite (with that column first) + possibly a separate single index
Columns are never queried togetherSeparate single-column indexes

Practical Design Example

Let's walk through designing composite indexes for real queries.

The Queries

-- Query 1: Find orders for a customer
SELECT * FROM orders WHERE customer_id = 'CUST_001';

-- Query 2: Find delivered orders for a customer
SELECT * FROM orders 
WHERE customer_id = 'CUST_001' AND order_status = 'delivered';

-- Query 3: Find recent delivered orders for a customer
SELECT * FROM orders 
WHERE customer_id = 'CUST_001' 
  AND order_status = 'delivered'
ORDER BY order_purchase_timestamp DESC;

The Index Design

-- One composite index serves ALL THREE queries:
CREATE NONCLUSTERED INDEX IX_Orders_Cust_Status_Date
ON orders (customer_id, order_status, order_purchase_timestamp DESC)
INCLUDE (order_delivered_timestamp);

Why this works:

QueryHow Index Is Used
Query 1Seeks on customer_id (leftmost prefix)
Query 2Seeks on customer_id + order_status (first two columns)
Query 3Seeks on first two columns; ORDER BY is free because order_purchase_timestamp DESC is the third key column

One well-designed composite index replaced what would have required multiple separate indexes.

Summary

Here is what you should remember about composite indexes:

ConceptKey Takeaway
WhatAn index on two or more columns
SortingSorted by first column, then second, then third...
Leftmost prefixIndex is only used if the query filters on the leftmost column(s)
Column orderPut the most commonly queried and most selective columns first
vs Single indexesOne composite index is better than multiple single indexes for multi-column queries
Max columnsUp to 16 key columns (plus INCLUDE columns)

Column Order Decision Checklist

  1. Which column appears in the most queries? → Put it first
  2. Which column has the highest selectivity? → Put it early
  3. Is there an ORDER BY? → Consider making it a key column
  4. Are there additional SELECT columns? → Use INCLUDE

In the next tutorial, you will learn about a fundamentally different approach to indexing — Columnstore Indexes.