SQL Indexes Explained: How They Work and When to Use Them
An index is what turns a slow table scan into a fast lookup. We tested one on 200,000 rows and show the numbers, the query plans, the traps that make an index useless and its cost on writes.
- An index is a sorted copy of one or more columns that lets the database find rows without reading the whole table.
- In our test, one lookup on 200,000 rows dropped from about 8 ms to about 0.003 ms after adding an index.
- In a composite index the column order matters: it helps queries that use the leading columns.
- Indexes speed up reads but slow down writes and use storage, so add them for real query patterns only.
A query that feels instant on 100 rows can crawl on 10 million. Most of the time the cure is not a bigger server. It is a SQL index on the right column.
This guide builds on our SQL tutorial for beginners. If you join tables a lot, read SQL joins explained too, because join columns are prime candidates for indexes.
What an SQL index is
Think of the index at the back of a book. Instead of reading every page to find “foreign key”, you look it up in the sorted list and jump to the page. A database index works the same way: a sorted structure, usually a B-tree, that stores column values and pointers to the rows.
Without an index the database does a full table scan and checks every row. With one, it does an index seek and goes straight to the matching rows.
CREATE INDEX idx_users_email ON users (email);
DROP INDEX idx_users_email;
Our 200,000-row test
We created a users table with 200,000 rows in SQLite 3.50 and looked up one email address. The EXPLAIN QUERY PLAN command shows how the database will run a query.
SELECT * FROM users WHERE email = 'user150000@example.com';
-- before: SCAN users (about 8.16 ms)
CREATE INDEX idx_users_email ON users (email);
-- after: SEARCH users USING INDEX idx_users_email (email=?) (about 0.003 ms)
The word SCAN means every row was read. SEARCH ... USING INDEX means the database jumped to the match. Times are averages from our run; yours will differ, but the gap grows with table size. Other databases print plans with EXPLAIN or EXPLAIN ANALYZE.
Composite indexes and column order
When queries filter on several columns, one index on both can beat two separate ones.
SELECT COUNT(*) FROM users WHERE country = 'IN' AND age > 60;
CREATE INDEX idx_country_age ON users (country, age);
-- before: about 10.81 ms after: about 0.35 ms
-- plan: SEARCH users USING COVERING INDEX idx_country_age (country=? AND age>?)
Order matters. This index works for filters on country, or on country and age. A query that filters only on age cannot seek into it. In our test it fell back to scanning the index, which is better than scanning the table but not a true lookup:
SELECT COUNT(*) FROM users WHERE age > 60;
-- plan: SCAN users USING COVERING INDEX idx_country_age
A good rule: put columns you compare with = first and a range column last.
What stops an index from being used
- A function on the column.
WHERE LOWER(email) = ...cannot use a plain index onemail. Our test plan showedSCAN users. - A leading wildcard.
WHERE email LIKE '%150000@example.com'forces a scan as well.LIKE 'user15%'can use an index. - Low-selectivity columns. An index on a yes/no column helps little because half of the rows match.
- Type mismatches. Comparing a number column with a string can disable the index in some databases.
Tip: to search case-insensitively with an index, store a lowercase copy of the value, or create an expression index if your database supports it, such as PostgreSQL.
The cost of indexes
Every index must be updated on each insert, update and delete, and it takes disk space. We inserted 20,000 rows into the same table once with our two extra indexes and once without them. The version with indexes was about twice as slow, in a test that took only a few hundredths of a second, so the exact numbers are small, but the direction is always the same.
| Index type | What it is | Good for |
|---|---|---|
| Primary key | Unique index that identifies each row | Lookups by ID, foreign keys |
| Unique index | Index that forbids duplicates | Emails, usernames |
| Composite index | Index on several columns | Queries that filter on those columns together |
| Covering index | Index that contains every column the query needs | Very fast reads without touching the table |
| Clustered index | Table rows stored in index order, in databases that support it | Range queries on one ordering |
When to add an index
- Find slow queries first, from logs or the query plan, not by guessing.
- Index columns used in
WHERE,JOINandORDER BYof frequent queries. - Prefer high-selectivity columns such as email or order number.
- Check the plan again after adding the index to confirm it is used.
- Drop indexes nobody uses. They still cost every write.
Testers can verify this too. Compare query times with and without data growth when you validate database behaviour, using the queries in our SQL interview questions for testers.
Final thoughts
Read the plan, add the smallest useful index and read the plan again. That loop will fix most slow queries without any other change.
Frequently asked questions
What is an index in SQL?
An index is a sorted data structure on one or more columns that lets the database find rows quickly without scanning the entire table.
Do indexes slow down inserts and updates?
Yes. Every index has to be kept in sync with the table, so writes become slower as you add indexes. That is the price for faster reads.
What is the difference between a clustered and a non-clustered index?
A clustered index decides the physical order of the rows in the table, so there is only one per table. A non-clustered index is a separate structure that points back to the rows.
Does column order matter in a composite index?
Yes. The index helps queries that use its leading columns. A filter on only the second column usually cannot seek into the index.
How can I check whether a query uses an index?
Run EXPLAIN, or EXPLAIN QUERY PLAN in SQLite, in front of the query. A scan means no index was used, while a seek or search shows the index name.
Keep learning
Need help with testing, WordPress or SEO?
We help teams ship better software and grow their sites. Tell us what you are working on and we will suggest a clear next step.