Analyze and optimize SQL queries for performance. Use when a user asks to optimize a query, speed up a slow query, analyze a query plan, add indexes, fix N+1 queries, reduce query time, tune database performance, or rewrite SQL for efficiency. Supports PostgreSQL, MySQL, and SQLite.
Analyze SQL queries for performance problems and produce optimized versions with appropriate indexes. Covers query rewriting, index recommendations, execution plan analysis, and common anti-patterns.
When a user asks you to optimize a SQL query or fix slow database performance, follow these steps:
Determine:
If you have access to the database, gather this yourself:
-- PostgreSQL: Check table sizes
SELECT relname AS table_name, reltuples::bigint AS row_count
FROM pg_class
WHERE relkind = 'r' AND relnamespace = (
SELECT oid FROM pg_namespace WHERE nspname = 'public'
)
ORDER BY reltuples DESC;
-- PostgreSQL: Check existing indexes
SELECT indexname, indexdef
FROM pg_indexes
WHERE tablename = 'target_table';
-- PostgreSQL
EXPLAIN ANALYZE
SELECT ...your query here...;
-- MySQL
EXPLAIN FORMAT=JSON
SELECT ...your query here...;
Look for these red flags in the plan:
Anti-pattern: SELECT * when you need specific columns
-- Before (fetches all columns, prevents covering index use)
SELECT * FROM orders WHERE status = 'pending';
-- After (fetch only needed columns)
SELECT id, customer_id, total, created_at
FROM orders WHERE status = 'pending';
Anti-pattern: Missing index on WHERE/JOIN columns
-- If this query is slow:
SELECT * FROM orders WHERE customer_id = 123 AND status = 'pending';
-- Add a composite index:
CREATE INDEX idx_orders_customer_status ON orders (customer_id, status);
Anti-pattern: N+1 queries in application code
-- Before: 1 query + N queries
SELECT id FROM orders WHERE date > '2024-01-01';
-- Then for each order:
SELECT * FROM order_items WHERE order_id = ?;
-- After: Single query with JOIN
SELECT o.id, o.total, oi.product_name, oi.quantity, oi.price
FROM orders o
JOIN order_items oi ON o.id = oi.order_id
WHERE o.date > '2024-01-01';
Anti-pattern: Functions on indexed columns
-- Before (cannot use index on created_at)
SELECT * FROM users WHERE YEAR(created_at) = 2024;
-- After (uses index on created_at)
SELECT * FROM users
WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01';
Anti-pattern: Correlated subquery that can be a JOIN
-- Before (executes subquery for every row)
SELECT name, (
SELECT COUNT(*) FROM orders WHERE orders.customer_id = customers.id
) AS order_count
FROM customers;
-- After (single pass with JOIN)
SELECT c.name, COUNT(o.id) AS order_count
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
GROUP BY c.id, c.name;
Anti-pattern: OFFSET for deep pagination
-- Before (scans and discards 10000 rows)
SELECT * FROM products ORDER BY id LIMIT 20 OFFSET 10000;
-- After (keyset pagination, uses index)
SELECT * FROM products WHERE id > 10000 ORDER BY id LIMIT 20;
Follow these rules for index design:
-- Composite index for: WHERE status = 'active' AND created_at > '2024-01-01' ORDER BY name
CREATE INDEX idx_users_status_created_name
ON users (status, created_at, name);
Present:
User request: "This query takes 45 seconds on 2M rows, can you speed it up?"
Original query:
SELECT c.name, c.email,
COUNT(*) AS order_count,
SUM(o.total) AS total_spent
FROM customers c, orders o
WHERE c.id = o.customer_id
AND YEAR(o.created_at) = 2024
AND o.status IN ('completed', 'shipped')
GROUP BY c.name, c.email
HAVING SUM(o.total) > 500
ORDER BY total_spent DESC;
Problems identified:
YEAR() function prevents index use on created_at(customer_id, status, created_at)Optimized query:
SELECT c.name, c.email,
COUNT(*) AS order_count,
SUM(o.total) AS total_spent
FROM customers c
JOIN orders o ON c.id = o.customer_id
WHERE o.created_at >= '2024-01-01'
AND o.created_at < '2025-01-01'
AND o.status IN ('completed', 'shipped')
GROUP BY c.id, c.name, c.email
HAVING SUM(o.total) > 500
ORDER BY total_spent DESC;
Index recommendations:
CREATE INDEX idx_orders_customer_status_date
ON orders (customer_id, status, created_at);
Expected result: ~45s down to ~0.5s with the index.
User request: "My Django view is slow, the debug toolbar shows 200+ queries"
Problem: ORM fetching related objects lazily in a loop.
Fix:
# Before: N+1 queries
orders = Order.objects.filter(status="pending")
for order in orders:
print(order.customer.name) # Each access = 1 query
# After: 2 queries total
orders = Order.objects.filter(status="pending").select_related("customer")
for order in orders:
print(order.customer.name) # Already loaded
EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT) for the most useful plan output.npx skills add TerminalSkills/sql-optimizer下载完整 Skill 目录,包含 SKILL.md 及所有相关文件
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer