agentleFS
SkillMedevSkillMedev/database-performance/skills/index-advisor/SKILL.md

index-advisor

Skill0 starsChanged 38 days agoMITView on GitHub

Recommends, orders, and prunes indexes for a specific query or table - composite column order, selectivity rules, partial and covering indexes, duplicate/unused cleanup, and write-amplification tradeoffs. Use when a query is slow and EXPLAIN shows a Seq Scan or a sort, before adding a CREATE INDEX, or when auditing a table's index set for bloat or duplicates. Do NOT use to diagnose an unknown slow query from scratch - start with sql-query-optimizer; do NOT use when the query shape itself is the problem (function-wrapped predicates, leading wildcards, correlated subqueries) - use query-rewriter instead; do NOT use when the table is too large and needs partitioning or a time-series strategy - use partition-planner instead.

2 other projects use a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

10 rules. About 0 are common, 8 are distinctive, and 2 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈8

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 2

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Make it partial when one predicate is always present: If queries always filter the same constant, scope the index: CREATE INDEX ON orders …

    line 25
  2. Distinctive:

    Every recommendation cites the plan line it fixes.

    line 54
  3. Distinctive:

    Every recommended index carries a selectivity estimate showing the predicate lands under the ~10% usefulness line.

    line 55
  4. Distinctive:

    Every drop names the evidence (idx_scan = 0, or the index it duplicates).

    line 57
  5. Distinctive:

    Do NOT index tiny tables (under a few hundred rows) - a Seq Scan beats an index lookup.

    line 62
  6. Distinctive:

    Do NOT index a predicate that matches more than ~10-20% of the table and expect the planner to use it.

    line 63
  7. Very specific:

    Do NOT index a column only ever used inside a function or with a leading % wildcard unless you build the matching expression index - …

    line 64
  8. Distinctive:

    Do NOT create a single-column index whose column is already the leftmost prefix of an existing composite.

    line 65
  9. Distinctive:

    Do NOT add an index without re-running the plan to confirm it is used and faster.

    line 66
  10. Distinctive:

    Do NOT solve a too-large table by indexing - that is partition-planner's job.

    line 67

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Index Advisor

An index trades write speed and disk for read speed. Add the fewest indexes that serve real query shapes, order their columns deliberately, and prove every change with a plan. The costly mistake is the speculative index: it fails to help the read (wrong column order, predicate too unselective) while permanently taxing every write. This skill owns index design only - plan diagnosis belongs to sql-query-optimizer, and query restructuring belongs to query-rewriter.

Inputs to collect

  1. The slow query text and its EXPLAIN (ANALYZE, BUFFERS) output (Postgres) or EXPLAIN ANALYZE / EXPLAIN FORMAT=JSON (MySQL). No plan, no recommendation.
  2. The table's row count and the existing index list (\d table / SHOW INDEX).
  3. Predicate selectivity: roughly what fraction of rows each WHERE predicate matches. Estimate from n_distinct in pg_stats or a quick COUNT(*) ratio; label estimates as guesses.
  4. The write profile: is this table write-hot (high INSERT/UPDATE rate), and which columns get updated?
  5. For an audit: pg_stat_user_indexes (or the MySQL equivalent) over a representative window.

Workflow

  1. Capture evidence first. Get the actual access path from the plan. Note the Seq Scan, expensive Sort, or rows-removed-by-filter that an index would eliminate. Never recommend an index without a plan that shows the problem.
  2. Check selectivity before designing anything. A B-tree index pays off when the predicate selects roughly under 5-10% of rows; once a query fetches more than ~10-20% of a table, the planner rightly prefers a Seq Scan and the index will sit unused. A column with very low cardinality (boolean, status, enum with a handful of values) is not worth a plain index - that is partial-index territory (step 4) or no index at all.
  3. Order composite columns: equality first, then one range/sort. Extract the query shape - equality predicates (=, IN), the one range/sort column (<, >, BETWEEN, ORDER BY), and the returned columns. Equality columns lead, then a single range or sort column. (tenant_id, created_at) serves WHERE tenant_id = ? ORDER BY created_at; reversing it does not. By the leftmost-prefix rule, (a, b, c) already serves (a) and (a, b) - so do not also create (a); it is redundant for lookups. Among equality columns, order for prefix reuse across the workload's query shapes.
  4. Make it partial when one predicate is always present. If queries always filter the same constant, scope the index: CREATE INDEX ON orders (created_at) WHERE status = 'pending' is tiny and hot. Prefer a partial index over a plain index on a low-cardinality column.
  5. Make it covering when the heap fetch dominates. To get an index-only scan: in Postgres add INCLUDE (amount, status); in MySQL InnoDB the PK is already appended, so add the covered columns to the index key. Confirm with the plan showing Index Only Scan (Postgres) or Using index (MySQL).
  6. Prune dead weight. Find unused indexes via pg_stat_user_indexes where idx_scan = 0 over a representative window (long enough to cover monthly jobs). Find duplicates by identical or prefix-redundant column lists and drop the subset. Apply with CREATE INDEX CONCURRENTLY / DROP INDEX CONCURRENTLY (Postgres) to avoid table locks.
  7. Weigh the write cost before keeping it. Every secondary index adds roughly one extra write per row change - five indexes means an insert does ~6x the index maintenance of an unindexed table - and bloats the WAL/redo log. Updating an indexed column defeats HOT/in-place updates, turning cheap updates into full row versions. More than ~5 indexes on a write-hot table is a red flag demanding per-index justification; prefer one well-ordered composite over several single-column indexes.
  8. Verify. Re-run the plan and confirm the planner uses the new index AND the query is actually faster (compare actual times, not costs). If it does not, drop the index.

Worked example

Query: SELECT id, total FROM orders WHERE tenant_id = 42 AND status = 'pending' ORDER BY created_at DESC LIMIT 20 on 40M rows.

Bad: CREATE INDEX ON orders (created_at); - the plan still filters 40M rows' worth of other tenants after the index scan; tenant_id (the selective equality, ~0.1% of rows) is not in the index, and status alone matches 30% of rows so it cannot carry the query either.

Good:

CREATE INDEX CONCURRENTLY idx_orders_tenant_pending
ON orders (tenant_id, created_at DESC)
INCLUDE (total)
WHERE status = 'pending';

Equality column leads, the sort column follows so the ORDER BY ... LIMIT 20 reads pre-sorted, the always-present status = 'pending' becomes a partial predicate keeping the index small, and INCLUDE (total) yields an Index Only Scan. Verified: plan goes from Seq Scan + Sort (4.8s) to Index Only Scan (2ms).

Deliverable

Produce an index change plan containing: each CREATE INDEX / DROP INDEX statement (CONCURRENTLY where supported), the plan line each addition fixes and the selectivity estimate justifying it, the evidence for each drop (idx_scan = 0 window or the index it duplicates), the net index count and write-cost delta for the table, and the before/after EXPLAIN ANALYZE comparison.

Quality bar

  • Every recommendation cites the plan line it fixes; no speculative indexes.
  • Every recommended index carries a selectivity estimate showing the predicate lands under the ~10% usefulness line.
  • Column order is justified by the equality-then-range rule, not guessed.
  • Every drop names the evidence (idx_scan = 0, or the index it duplicates).
  • Net index count and write cost are stated, not just the additions.

Do NOT

  • Do NOT index tiny tables (under a few hundred rows) - a Seq Scan beats an index lookup.
  • Do NOT index a predicate that matches more than ~10-20% of the table and expect the planner to use it.
  • Do NOT index a column only ever used inside a function or with a leading % wildcard unless you build the matching expression index - otherwise the planner cannot use it.
  • Do NOT create a single-column index whose column is already the leftmost prefix of an existing composite.
  • Do NOT add an index without re-running the plan to confirm it is used and faster.
  • Do NOT solve a too-large table by indexing - that is partition-planner's job; do NOT rewrite the query shape - that is query-rewriter's job.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.