agentsclimarketplace

Writing performant queries

Skill pumarogie/claude-postgres-skills/skills/writing-performant-queries

Production Postgres survival skills for Claude Code — schema design, safe migrations, query performance, connection pooling, autovacuum/bloat, and queue/partitioning patterns.

Install
npx -y skills add pumarogie/claude-postgres-skills --skill writing-performant-queries

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • 15 days oldThe repository was created 15 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Guides evidence-first Postgres query diagnosis when an API or database becomes slow, an expensive query must be found, EXPLAIN must be used safely, the planner ignores an index, statistics may be stale, or a filter-and-sort query needs an index.

SKILL.md

4.5 KB, 905 tokens by cl100k_base, as published. Nobody here has run it

Writing Performant Queries

Required diagnostic sequence

Follow these steps in order. If the user already provides the query and plan, start at step 2. Never propose an index before identifying which query is slow and inspecting evidence from its plan.

1. Find the expensive query

Use pg_stat_statements to rank normalized SQL before tuning anything. total_exec_time finds aggregate database load; mean_exec_time finds individually slow calls. Compare a defined time window and note when statistics were reset.

SELECT queryid, calls, total_exec_time, mean_exec_time, rows,
       left(query, 200) AS query
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 20;

If it is not installed, add pg_stat_statements to the existing comma-separated shared_preload_libraries, restart PostgreSQL, and create the extension in each database. Do not guess from a generic “the API is slow” report.

2. Inspect that query's plan safely

Use plain EXPLAIN first; it plans but does not execute the statement. Use EXPLAIN (ANALYZE, BUFFERS) only when executing the query is safe and representative.

Warning: EXPLAIN ANALYZE executes the statement. Never run it casually on a production INSERT, UPDATE, or DELETE; it performs writes, takes locks, and can trigger side effects. Prefer staging or a safe read-only reproduction for write queries.

EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM tasks
WHERE tenant_id = $1 AND status = 'pending'
ORDER BY created_at DESC
LIMIT 50;

Read nodes from the inside out. Compare estimated rows with actual rows and inspect loops, buffer reads, sorts, and rows removed by filters. Estimated cost is not elapsed milliseconds.

3. Check statistics before changing indexes

Always check for stale or insufficient planner statistics before assuming an index is missing. A large estimated-versus-actual row mismatch is the signal. Run ANALYZE on the affected table, then inspect the plan again:

ANALYZE tasks;

Raise a column's statistics target only when measured skew or correlation still produces bad estimates. A sequential scan may be the correct plan for a small table or a query returning a large fraction of the rows.

Never use enable_seqscan=off or another global planner override as the fix. Diagnose estimates, selectivity, and index column order instead.

4. Change the query or add the smallest useful index

Derive indexes from the identified query's actual predicates and ordering. For equality filters followed by ordering, put equality columns first and the ordered column next:

CREATE INDEX CONCURRENTLY idx_tasks_tenant_status_created
ON tasks (tenant_id, status, created_at DESC);

This index supports tenant_id = ... AND status = ... ORDER BY created_at DESC LIMIT ... without a separate sort. Do not propose separate single-column indexes as the primary answer for this combined access path.

Before creating it, inspect existing indexes and do not add one already covered by an equivalent left prefix. Match composite column order to the query: an index on (tenant_id, created_at) only helps predicates that can use its leftmost ordering. B-trees can scan in either direction; explicit direction matters most for mixed-direction ordering.

Use CREATE INDEX CONCURRENTLY for a live table and follow writing-safe-migrations for lock_timeout, transaction restrictions, and invalid-index cleanup. Every index consumes disk and adds write and vacuum cost.

5. Verify the result

Re-run the same plan and workload window. Confirm improved actual time and buffers without unacceptable write cost. Optimize total workload cost, not one anecdotal call, and remove redundant indexes only after observing a representative workload.

Additional rules

  • Index selective filters and join keys used by hot queries; foreign keys do not automatically index referencing columns.
  • Parameterize values. Diagnose generic prepared plans before changing planner settings.
  • Treat high sequential-scan counts as a signal, not proof; reporting and bulk reads often should scan.
  • For unbounded time-series data whose hot queries prune by time, consider partitioning with postgres-advanced-patterns.

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.