Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Optimize SQL queries with index strategies, query plan analysis, and rewrite patterns for faster database performance.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
SKILL.md
1---2name: sql-optimization-patterns3description: Master SQL query optimization, indexing strategies, and EXPLAIN analysis to dramatically improve database performance and eliminate slow queries. Use when debugging slow queries, designing database schemas, or optimizing application performance.4---56# SQL Optimization Patterns78Transform slow database queries into lightning-fast operations through systematic optimization, proper indexing, and query plan analysis.910## When to Use This Skill1112- Debugging slow-running queries13- Designing performant database schemas14- Optimizing application response times15- Reducing database load and costs16- Improving scalability for growing datasets17- Analyzing EXPLAIN query plans18- Implementing efficient indexes19- Resolving N+1 query problems2021## Core Concepts2223### 1. Query Execution Plans (EXPLAIN)2425Understanding EXPLAIN output is fundamental to optimization.2627**PostgreSQL EXPLAIN:**2829```sql30-- Basic explain31EXPLAIN SELECT * FROM users WHERE email = '[email protected]';3233-- With actual execution stats34EXPLAIN ANALYZE35SELECT * FROM users WHERE email = '[email protected]';3637-- Verbose output with more details38EXPLAIN (ANALYZE, BUFFERS, VERBOSE)39SELECT u.*, o.order_total40FROM users u41JOIN orders o ON u.id = o.user_id42WHERE u.created_at > NOW() - INTERVAL '30 days';43```4445**Key Metrics to Watch:**4647- **Seq Scan**: Full table scan (usually slow for large tables)48- **Index Scan**: Using index (good)49- **Index Only Scan**: Using index without touching table (best)50- **Nested Loop**: Join method (okay for small datasets)51- **Hash Join**: Join method (good for larger datasets)52- **Merge Join**: Join method (good for sorted data)53- **Cost**: Estimated query cost (lower is better)54- **Rows**: Estimated rows returned55- **Actual Time**: Real execution time5657### 2. Index Strategies5859Indexes are the most powerful optimization tool.6061**Index Types:**6263- **B-Tree**: Default, good for equality and range queries64- **Hash**: Only for equality (=) comparisons65- **GIN**: Full-text search, array queries, JSONB66- **GiST**: Geometric data, full-text search67- **BRIN**: Block Range INdex for very large tables with correlation6869```sql70-- Standard B-Tree index71CREATE INDEX idx_users_email ON users(email);7273-- Composite index (order matters!)74CREATE INDEX idx_orders_user_status ON orders(user_id, status);7576-- Partial index (index subset of rows)77CREATE INDEX idx_active_users ON users(email)78WHERE status = 'active';7980-- Expression index81CREATE INDEX idx_users_lower_email ON users(LOWER(email));8283-- Covering index (include additional columns)84CREATE INDEX idx_users_email_covering ON users(email)85INCLUDE (name, created_at);8687-- Full-text search index88CREATE INDEX idx_posts_search ON posts89USING GIN(to_tsvector('english', title || ' ' || body));9091-- JSONB index92CREATE INDEX idx_metadata ON events USING GIN(metadata);93```9495### 3. Query Optimization Patterns9697**Avoid SELECT \*:**9899```sql100-- Bad: Fetches unnecessary columns101SELECT * FROM users WHERE id = 123;102103-- Good: Fetch only what you need104SELECT id, email, name FROM users WHERE id = 123;105```106107**Use WHERE Clause Efficiently:**108109```sql110-- Bad: Function prevents index usage111SELECT * FROM users WHERE LOWER(email) = '[email protected]';112113-- Good: Create functional index or use exact match114CREATE INDEX idx_users_email_lower ON users(LOWER(email));115-- Then:116SELECT * FROM users WHERE LOWER(email) = '[email protected]';117118-- Or store normalized data119SELECT * FROM users WHERE email = '[email protected]';120```121122**Optimize JOINs:**123124```sql125-- Bad: Cartesian product then filter126SELECT u.name, o.total127FROM users u, orders o128WHERE u.id = o.user_id AND u.created_at > '2024-01-01';129130-- Good: Filter before join131SELECT u.name, o.total132FROM users u133JOIN orders o ON u.id = o.user_id134WHERE u.created_at > '2024-01-01';135136-- Better: Filter both tables137SELECT u.name, o.total138FROM (SELECT * FROM users WHERE created_at > '2024-01-01') u139JOIN orders o ON u.id = o.user_id;140```141142## Detailed patterns and worked examples143144Detailed pattern documentation lives in `references/details.md`. Read that file when the navigation tier above is insufficient.145146## Best Practices1471481. **Index Selectively**: Too many indexes slow down writes1492. **Monitor Query Performance**: Use slow query logs1503. **Keep Statistics Updated**: Run ANALYZE regularly1514. **Use Appropriate Data Types**: Smaller types = better performance1525. **Normalize Thoughtfully**: Balance normalization vs performance1536. **Cache Frequently Accessed Data**: Use application-level caching1547. **Connection Pooling**: Reuse database connections1558. **Regular Maintenance**: VACUUM, ANALYZE, rebuild indexes156157```sql158-- Update statistics159ANALYZE users;160ANALYZE VERBOSE orders;161162-- Vacuum (PostgreSQL)163VACUUM ANALYZE users;164VACUUM FULL users; -- Reclaim space (locks table)165166-- Reindex167REINDEX INDEX idx_users_email;168REINDEX TABLE users;169```170171## Common Pitfalls172173- **Over-Indexing**: Each index slows down INSERT/UPDATE/DELETE174- **Unused Indexes**: Waste space and slow writes175- **Missing Indexes**: Slow queries, full table scans176- **Implicit Type Conversion**: Prevents index usage177- **OR Conditions**: Can't use indexes efficiently178- **LIKE with Leading Wildcard**: `LIKE '%abc'` can't use index179- **Function in WHERE**: Prevents index usage unless functional index exists180181## Monitoring Queries182183```sql184-- Find slow queries (PostgreSQL)185SELECT query, calls, total_time, mean_time186FROM pg_stat_statements187ORDER BY mean_time DESC188LIMIT 10;189190-- Find missing indexes (PostgreSQL)191SELECT192schemaname,193tablename,194seq_scan,195seq_tup_read,196idx_scan,197seq_tup_read / seq_scan AS avg_seq_tup_read198FROM pg_stat_user_tables199WHERE seq_scan > 0200ORDER BY seq_tup_read DESC201LIMIT 10;202203-- Find unused indexes (PostgreSQL)204SELECT205schemaname,206tablename,207indexname,208idx_scan,209idx_tup_read,210idx_tup_fetch211FROM pg_stat_user_indexes212WHERE idx_scan = 0213ORDER BY pg_relation_size(indexrelid) DESC;214```215