
Database Optimizer
Diagnose slow PostgreSQL or MySQL workloads and add the right B-tree and composite indexes from real query and scan statistics.
Overview
database-optimizer is an agent skill for the Operate phase that guides PostgreSQL and MySQL index selection using statement stats and scan heuristics to cut slow-query time.
Install
npx skills add https://github.com/jeffallan/claude-skills --skill database-optimizerWhat is this skill?
- PostgreSQL pg_stat_statements and seq_scan heuristics to rank index candidates
- B-tree patterns for WHERE, JOIN, ORDER BY, and UNIQUE constraints
- Multi-column index column-order rules with explicit anti-patterns
- MySQL sys.statements_with_full_table_scans discovery queries
- Executable SQL snippets for index create and candidate identification
Adoption & trust: 3.3k installs on skills.sh; 9.7k GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
Your API feels fine in dev but production queries pile up because you cannot see which tables need indexes or how to order composite keys.
Who is it for?
Solo builders maintaining a Postgres or MySQL app who have slow endpoints and can run diagnostic SQL against real traffic patterns.
Skip if: Teams on managed-only databases with no stat views, greenfield schema design with zero metrics yet, or NoSQL/document stores this skill does not cover.
When should I use this skill?
Production or staging PostgreSQL/MySQL shows slow queries, high sequential scans, or you are planning index migrations after load testing.
What do I get? / Deliverables
You get prioritized index candidates, ready-to-run CREATE INDEX statements, and clearer rules for when multi-column indexes actually help the queries you run.
- Ranked list of index candidates tied to measured queries
- CREATE INDEX / UNIQUE INDEX SQL aligned to filter, join, and sort patterns
Recommended Skills
Journey fit
Canonical shelf is Operate because the skill centers on production stats (pg_stat_statements, seq scans) and iterative tuning after the app is live—not greenfield schema sketching alone. Iterate fits ongoing performance work: find hotspots, add indexes, re-measure, and refine without a full redeploy narrative.
How it compares
Use for hands-on index SQL and methodology instead of guessing indexes from ORM logs alone or waiting on generic "add an index on id" advice.
Common Questions / FAQ
Who is database-optimizer for?
Indie and solo backend builders on PostgreSQL or MySQL who ship their own API or SaaS and need to fix query latency without a dedicated DBA.
When should I use database-optimizer?
Use it in Operate when p95 latency spikes after launch, during iterative perf passes on staging mirrors of production stats, or when ship-time load tests expose seq scans on large tables.
Is database-optimizer safe to install?
Treat it like any third-party skill: review the Security Audits panel on this Prism page, and never let an agent run destructive DDL on production without your review and backups.
SKILL.md
READMESKILL.md - Database Optimizer
# Index Strategies ## Index Selection Methodology ### Identify Index Candidates ```sql -- PostgreSQL: Find queries missing indexes SELECT query, calls, total_exec_time, mean_exec_time FROM pg_stat_statements WHERE mean_exec_time > 100 ORDER BY total_exec_time DESC LIMIT 20; -- PostgreSQL: Find sequential scans on large tables SELECT schemaname, tablename, seq_scan, seq_tup_read, idx_scan, seq_tup_read / seq_scan as avg_seq_tup_read FROM pg_stat_user_tables WHERE seq_scan > 0 AND seq_tup_read / seq_scan > 10000 ORDER BY seq_tup_read DESC; -- MySQL: Check table scans SELECT * FROM sys.statements_with_full_table_scans WHERE db = 'your_database' ORDER BY exec_count DESC; ``` ## B-Tree Indexes (Default) ### Single Column Indexes ```sql -- Create index for WHERE clauses CREATE INDEX idx_users_email ON users(email); -- Create index for JOIN conditions CREATE INDEX idx_orders_user_id ON orders(user_id); -- Create index for ORDER BY CREATE INDEX idx_products_price ON products(price); -- Unique constraint as index CREATE UNIQUE INDEX idx_users_username ON users(username); ``` ### Multi-Column Indexes ```sql -- Order matters: most selective column first CREATE INDEX idx_orders_status_created ON orders(status, created_at); -- Good for queries: -- WHERE status = 'pending' -- WHERE status = 'pending' AND created_at > '2024-01-01' -- WHERE status = 'pending' ORDER BY created_at -- NOT good for: -- WHERE created_at > '2024-01-01' (status not specified) -- Include commonly queried columns CREATE INDEX idx_users_active_email_name ON users(active, email) INCLUDE (name); ``` ### Column Order Guidelines ```sql -- Rule 1: Equality before range CREATE INDEX idx_events_type_timestamp ON events(type, timestamp); -- type = 'click' AND timestamp > ... -- Rule 2: High selectivity first CREATE INDEX idx_orders_user_status ON orders(user_id, status); -- user_id is more selective than status -- Rule 3: Match query patterns -- Query: WHERE country = 'US' AND city = 'NYC' AND zip = '10001' CREATE INDEX idx_locations_country_city_zip ON locations(country, city, zip); ``` ## Covering Indexes ### PostgreSQL INCLUDE Clause ```sql -- Include non-key columns for index-only scans CREATE INDEX idx_users_email_covering ON users(email) INCLUDE (name, created_at); -- Query can be satisfied entirely from index EXPLAIN (ANALYZE, BUFFERS) SELECT name, created_at FROM users WHERE email = 'user@example.com'; -- Should show "Index Only Scan" ``` ### MySQL Covering Indexes ```sql -- MySQL: Add columns to end of index CREATE INDEX idx_orders_user_covering ON orders(user_id, status, created_at, total); -- Query uses covering index EXPLAIN SELECT status, created_at, total FROM orders WHERE user_id = 123; -- Should show "Using index" in Extra column ``` ## Partial Indexes ### PostgreSQL Partial Indexes ```sql -- Index only active users CREATE INDEX idx_users_active_email ON users(email) WHERE active = true; -- Index only recent orders CREATE INDEX idx_orders_recent ON orders(user_id, created_at) WHERE created_at > NOW() - INTERVAL '30 days'; -- Index only pending/processing orders (ignore completed) CREATE INDEX idx_orders_active ON orders(status, user_id) WHERE status IN ('pending', 'processing'); -- Smaller index = better performance + less storage ``` ### MySQL Filtered Indexes (8.0+) ```sql -- MySQL 8.0+ supports functional indexes for similar effect CREATE INDEX idx_users_active ON users((CASE WHEN active = 1 THEN email END)); ``` ## Expression Indexes ### PostgreSQL Function Indexes ```sql -- Index for case-insensitive search CREATE INDEX idx_users_email_lower ON users(LOWER(email)); -- Query must match expression SELECT * FROM users WHERE LOWER(email) = LOWER('User@Example.com'); -- Index for JSONB queries CREATE INDEX idx_users_settings_theme ON users((settings->>'theme')); SELECT * FROM users WHERE settings->>'theme' = 'dark'; -- Index for date truncation CREATE INDEX idx_orders_date ON orders(DATE(created_at)); ``` ##