An index is a database object that keeps column values sorted so the optimizer can locate rows quickly instead of scanning the whole table. Faster reads come with a cost: every INSERT, UPDATE, or DELETE must also update the index.
WHERE filter or JOIN condition, the optimizer can traverse the index path rather than perform a table scan.Always compare execution plans before and after indexing to validate improvements.
EXPLAIN SELECT * FROM employees WHERE department_id = 5;
Plan output highlights whether the engine performs a sequential scan or an Index Scan/Index Only Scan.
CREATE INDEX idx_emp_dept ON employees (department_id);
-- Use indexes on columns that:
-- • Appear in WHERE, JOIN, or ORDER BY clauses
-- • Have high selectivity (many unique values)
CREATE INDEX idx_emp_dept_name ON employees (department_id, last_name);
-- Order matters: (department_id, last_name) ≠ (last_name, department_id)
-- Without index (slow)
SELECT * FROM orders WHERE customer_id = 101;
-- Create index
CREATE INDEX idx_orders_custid ON orders (customer_id);
-- Now query becomes faster
SELECT * FROM orders WHERE customer_id = 101;
-- Validate improvement with EXPLAIN
| Type | Description |
|---|---|
| Clustered Index | Reorders table data physically (only one per table). |
| Non-Clustered Index | Separate structure storing key-value pointers to rows. |
| Unique Index | Prevents duplicate values in indexed columns. |
| Composite Index | Includes multiple columns; order defines usefulness. |
| Full-Text Index | Optimized for searching text-heavy fields. |
| Bitmap Index | Great for low-cardinality columns in warehouses. |
CREATE INDEX idx_emp_cover ON employees (department_id, salary);
SELECT department_id, salary
FROM employees
WHERE department_id = 10;
-- Query runs entirely from the index
When all referenced columns are in the index, the engine can skip table lookups, improving performance.
DROP INDEX idx_emp_dept;
-- Monitor usage:
-- • sys.dm_db_index_usage_stats (SQL Server)
-- • pg_stat_user_indexes (PostgreSQL)
-- • EXPLAIN PLAN (Oracle/MySQL)
Regularly review usage stats to remove unused indexes that slow down write-heavy workloads.
| Tip | Description |
|---|---|
| Use selective columns | Index columns with high uniqueness to maximize index effectiveness. |
| Avoid indexing tiny tables | Sequential scans can be faster on small datasets. |
| Composite order matters | Place the most selective column first. |
| Limit total indexes | Excessive indexes slow down writes and increase storage. |
| Rebuild regularly | Use ALTER INDEX REBUILD or VACUUM ANALYZE to fight fragmentation. |
| Test with EXPLAIN | Compare plans before vs. after indexing to confirm gains. |
-- Step 1 – Original query
SELECT * FROM sales WHERE region = 'West' AND amount > 5000;
-- Step 2 – Create index
CREATE INDEX idx_sales_region_amt ON sales (region, amount);
-- Step 3 – Check performance
EXPLAIN ANALYZE SELECT * FROM sales WHERE region = 'West' AND amount > 5000;
-- Expect Index Scan instead of Seq Scan
| Concept | Summary |
|---|---|
| Index | Improves query read performance by enabling seeks. |
| Cost | Adds overhead to write operations. |
| Tools | Use EXPLAIN/EXPLAIN ANALYZE to validate usage. |
| Strategy | Index high-selectivity columns and common filters. |
| Maintenance | Monitor usage and rebuild/refresh statistics periodically. |