| name | sql-perf |
| description | SQL performance patterns - indexing and query optimization |
/sql-perf — SQL Performance
Channel Markus Winand: author of "SQL Performance Explained" and use-the-index-luke.com.
Core Philosophy
"Indexes are the key to SQL performance. Understand how they work, not just how to create them."
The Indexing Mantra: The database doesn't know what you want. Help it by designing proper indexes.
How Indexes Work
B-Tree Structure
[50]
/ \
[25] [75]
/ \ / \
[10,20] [30,40] [60,70] [80,90]
↓
Leaf nodes contain row pointers
- Logarithmic access: O(log n) to find any value
- Range scans: Efficient once you find the start
- Ordering: Index is pre-sorted
Index Lookup Process
- INDEX RANGE SCAN: Traverse B-tree to find matching entries
- TABLE ACCESS BY ROWID: Fetch actual rows from table
- Problem: Step 2 is random I/O (expensive)
The Golden Rules
1. Index Columns in WHERE Clause
SELECT * FROM orders WHERE status = 'shipped';
SELECT * FROM orders WHERE UPPER(status) = 'SHIPPED';
CREATE INDEX idx_orders_status_upper ON orders (UPPER(status));
2. Leftmost Prefix Rule (Composite Indexes)
WHERE a = 1
WHERE a = 1 AND b = 2
WHERE a = 1 AND b = 2 AND c = 3
WHERE b = 2
WHERE a = 1 AND c = 3
3. Range Conditions Stop Index Usage
WHERE date = '2024-01-01' AND status = 'shipped'
WHERE date > '2024-01-01' AND status = 'shipped'
WHERE status = 'shipped' AND date > '2024-01-01'
Index Types for Common Queries
Equality + Range
Sorting
Covering Indexes
CREATE INDEX idx_users_email_covering ON users (email) INCLUDE (id, name);
EXPLAIN Analysis
Key Metrics
EXPLAIN ANALYZE SELECT * FROM orders WHERE customer_id = 1;
| Metric | Good | Bad |
|---|
| Type | eq_ref, ref, range | ALL (full scan) |
| Rows | Small number | Large number |
| Extra | Using index | Using filesort, Using temporary |
Common Issues
| Problem | Symptom | Fix |
|---|
| Full table scan | type: ALL | Add appropriate index |
| Filesort | Extra: Using filesort | Index for ORDER BY |
| Temporary table | Extra: Using temporary | Optimize GROUP BY |
| Index not used | Key: NULL | Check index, query |
Anti-Patterns
1. Too Many Indexes
CREATE INDEX idx_a ON t(a);
CREATE INDEX idx_b ON t(b);
CREATE INDEX idx_c ON t(c);
CREATE INDEX idx_abc ON t(a, b, c);
2. Low Selectivity Indexes
CREATE INDEX idx_active ON users(is_active);
CREATE INDEX idx_active_users ON users(id) WHERE is_active = true;
3. LIKE with Leading Wildcard
WHERE name LIKE '%smith%'
WHERE MATCH(name) AGAINST('smith')
WHERE name LIKE 'smith%'
Pagination Done Right
Offset Pagination (AVOID for large offsets)
SELECT * FROM posts ORDER BY created_at DESC LIMIT 10 OFFSET 10000;
Keyset Pagination (USE THIS)
SELECT * FROM posts
WHERE created_at < '2024-01-15 10:30:00'
ORDER BY created_at DESC
LIMIT 10;
Partial Indexes
CREATE INDEX idx_active_orders ON orders(customer_id)
WHERE status = 'active';
References
- "SQL Performance Explained" - Markus Winand
- use-the-index-luke.com - Free online resource
- modern-sql.com - Modern SQL features