Composite Indexes
A composite index (also called a multi-column or compound index) is a single index built across more than one column, rather than one column alone. It is useful whenever queries commonly filter or sort by the same combination of columns together, and it behaves differently from simply creating several separate single-column indexes.
Creating a composite index
A composite index on two columns
CREATE TABLE orders ( order_id INT PRIMARY KEY, customer_id INT, status VARCHAR(20), order_date DATE ); CREATE INDEX idx_orders_customer_status ON orders (customer_id, status);
Column order matters: the leftmost prefix rule
A composite index is internally sorted first by its first column, then by its second column within each value of the first, and so on — much like a phone book sorted by last name, then first name within each last name. Because of that structure, the index can be used efficiently for queries that filter on a leading prefix of its columns, but not for queries that skip the first column and only filter on a later one.
What idx_orders_customer_status (customer_id, status) can and cannot help with
-- Can use the index efficiently: filters on the leftmost column SELECT * FROM orders WHERE customer_id = 42; -- Can use the index efficiently: filters on both columns, in order SELECT * FROM orders WHERE customer_id = 42 AND status = 'shipped'; -- Cannot use this index efficiently: skips customer_id entirely SELECT * FROM orders WHERE status = 'shipped';
That last query would need its own index on status alone (or one where status is the leftmost column) to benefit from an index at all. This is why column order in a composite index should be chosen based on real query patterns: put the column most commonly filtered alone, or filtered first, on the left.
Worked example: index order matching query order
Composite index designed around an actual query
-- Frequent query: recent orders for a given customer SELECT * FROM orders WHERE customer_id = 42 ORDER BY order_date DESC; -- A composite index matching both the filter and the sort CREATE INDEX idx_orders_customer_date ON orders (customer_id, order_date);
With this index, the database can jump straight to customer 42's rows, and because order_date is already sorted within that group, it can also satisfy the ORDER BY without a separate sorting step.
Composite index vs multiple single-column indexes
Situation | Better choice |
|---|---|
Queries almost always filter on customer_id and status together | One composite index (customer_id, status) |
Queries sometimes filter on customer_id alone, sometimes on status alone, rarely both | Two separate single-column indexes |
Queries filter on customer_id and sort by order_date | One composite index (customer_id, order_date), covering both the filter and the sort |
A composite index spans multiple columns and is sorted by the first column, then the second within it, and so on.
The leftmost prefix rule means a composite index only helps queries that filter starting from its first column.
Order composite index columns to match real, common query filters and sorts — the most selective or most commonly filtered-alone column usually goes first.
A well-designed composite index can make a separate single-column index on its leading column unnecessary.