☰ Learn SQL Tutorial Menu
Indexes and Query Performance
Written by CSA mentors · Updated 16 Sept 2026 · 3 min read
As tables grow to millions of rows, some queries that were instant at 100 rows can become painfully slow. Indexes are the primary tool for keeping queries fast.
The analogy: a book index
Without an index, finding a topic in a book means reading every page — a "full table scan." A book's index lets you jump directly to the right page. A database index works the same way for a column's values.
Creating an index
CREATE INDEX idx_employees_department_id ON employees (department_id);
Now, a query filtering on department_id can jump directly to matching rows instead of scanning the entire table.
Primary keys and unique columns are indexed automatically
You never need to manually index a PRIMARY KEY or UNIQUE column — the database creates that index for you automatically, since it has to enforce uniqueness anyway.
Composite (multi-column) indexes
CREATE INDEX idx_orders_customer_status ON orders (customer_id, status);
Useful when you frequently filter on both columns together. Column order matters — this index helps queries filtering by customer_id alone, or by customer_id AND status, but not by status alone.
The tradeoff: indexes aren't free
- They speed up
SELECTqueries that filter, join, or sort on the indexed column(s). - They slow down
INSERT/UPDATE/DELETE, since every index also has to be updated when the underlying data changes. - They use additional disk space.
The rule of thumb: index columns you frequently search, filter, or join on — especially foreign keys — but don't index everything "just in case."
Seeing what the database will actually do: EXPLAIN
EXPLAIN SELECT * FROM employees WHERE department_id = 1;
-- PostgreSQL: adds actual timing
EXPLAIN ANALYZE SELECT * FROM employees WHERE department_id = 1;
EXPLAIN shows the query's execution plan — whether it's using an index or scanning the whole table — the essential tool for diagnosing a slow query.
Dropping an index
DROP INDEX idx_employees_department_id;Lesson 25 of 27
Sign in to track your progress and earn learning points for every lesson you finish.
Example
-- A ready-to-query practice database is loaded automatically before every run —
-- no CREATE TABLE needed. Available tables:
-- departments (id, name, location)
-- employees (id, first_name, last_name, email, department_id, job_title, salary, hire_date, manager_id)
-- customers (id, customer_name, city, country, signup_date)
-- products (id, product_name, category, price, stock_quantity)
-- orders (id, customer_id, employee_id, order_date, status)
-- order_items (id, order_id, product_id, quantity, unit_price)
SELECT * FROM employees WHERE department_id = 1;
