Azpg explain analyze
Skill lestermarch/postgres-ai-experts/skills/azpg-explain-analyze
Composable AI agents and skills for operating Azure Database for PostgreSQL Flexible Server - PostgreSQL can be used for everything.
npx -y skills add lestermarch/postgres-ai-experts --skill azpg-explain-analyzeAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
3 things to look at
- 20 days oldThe repository was created 20 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.
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 0 stars0 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
Read and interpret PostgreSQL query plans on Azure Database for PostgreSQL Flexible Server. Use this skill whenever the task involves EXPLAIN or EXPLAIN ANALYZE, understanding why a query is slow, reading a plan (seq scan vs index scan, nested loop vs hash vs merge join, sort/hash spills to disk), spotting bad row estimates or stale statistics, interpreting BUFFERS / shared hit vs read, finding the expensive node, or deciding whether a query needs an index, a rewrite, more work_mem, or an ANALYZE — even when the user just pastes a plan and asks "why is this slow?" or "what does this EXPLAIN mean?". Read-only: this skill diagnoses, it does not mutate.
SKILL.md
8.2 KB, as published. Nobody here has run it
Reading query plans on Azure Database for PostgreSQL Flexible Server
A query plan is the optimizer's chosen strategy. Reading it well means finding the one node that dominates the time, understanding why the planner chose it (usually its row estimate), and deciding the fix: an index, a rewrite, updated statistics, or more memory.
This skill is read-only and auto-runnable. EXPLAIN builds a plan without
executing; EXPLAIN (ANALYZE, BUFFERS) does execute the query — safe for
SELECT, but for INSERT/UPDATE/DELETE it performs the write, so wrap those in a
transaction you roll back (see Safety protocol). It never
creates indexes or changes config — for that, hand off to azpg-index-selection
or azpg-config-tuning.
Deep node-by-node reference is in
reference.md; the Flexible-Server timing caveats (managed I/O, shared buffers vs storage, remote latency) are inazure-constraints.md.
Live instance context (dynamic injection)
PGCONN is a libpq connection string. All read-only.
- Postgres major version (plan/option availability):
!
psql "$PGCONN" -tAc "SHOW server_version;" 2>/dev/null || echo "(unknown — ask for connection details)" - Memory that governs sort/hash spills (
Sort Method: external merge= spilled): !psql "$PGCONN" -tAc "SHOW work_mem;" 2>/dev/null || echo "(unknown)" - Tables with stale stats (autovacuum hasn't analyzed recently → bad estimates):
!
psql "$PGCONN" -tAc "SELECT relname, last_analyze, last_autoanalyze FROM pg_stat_user_tables ORDER BY n_mod_since_analyze DESC NULLS LAST LIMIT 5;" 2>/dev/null || echo "(unknown)"
When to use this skill
Trigger on: "why is this query slow?", "what does this EXPLAIN mean?", "read this
plan", "is this using my index?", "why a seq scan / nested loop?", "the estimate
is way off", "is it spilling to disk?", "interpret BUFFERS". If the conclusion is
"add an index" hand off to azpg-index-selection; if it's "raise work_mem /
tune the server" hand off to azpg-config-tuning; for aggregate slow-query
forensics across the whole workload use azpg-stat-diagnostics.
Decision flow
All steps are read-only.
- Capture the real plan. Run
EXPLAIN (ANALYZE, BUFFERS)— never optimize fromEXPLAINalone (estimates ≠ reality). Usescripts/explain_query.sqlfor the safe rollback wrapper on writes. - Find the dominant node. Read inside-out / bottom-up; find where actual time × loops concentrates. That node is the target — ignore cheap ones.
- Check the estimate. Compare
rows=(estimated) vsactual rows. A large gap (e.g. 10×+) means the planner is flying blind → stale stats, correlated predicates, or a non-sargable expression. - Classify the problem using the table below and
reference.md. - Route the fix — index (
azpg-index-selection), rewrite (here), stats (ANALYZE), or memory/config (azpg-config-tuning).
Symptom → likely cause → fix
| In the plan | Likely cause | Fix / route |
|---|---|---|
Seq Scan on a big table with a selective filter | no usable index, or predicate not sargable | index it → azpg-index-selection; make predicate sargable (below) |
rows estimate ≫ or ≪ actual rows | stale statistics / correlated columns | ANALYZE <table>; consider CREATE STATISTICS for correlations |
Nested Loop with high loops on inner side | planner underestimated outer rows | fix the estimate (stats); an index on the inner join key helps |
Sort Method: external merge Disk: …kB | work_mem too low for this sort | raise work_mem (session or server) → azpg-config-tuning |
Hash Batches: >1 / Disk Usage on Hash | hash spilled — work_mem too low | same as above |
Rows Removed by Filter: large | reading rows then throwing them away | index the filter, or a partial index |
Heap Fetches: high on an Index Only Scan | visibility map stale | VACUUM <table> |
Index Scan but still slow | low selectivity or wide heap fetches | covering index, or accept the scan |
| Big gap between planning time and execution | many partitions / complex plan | check partition pruning, generic vs custom plans |
Making a predicate sargable (common rewrites)
The planner can only use an index when the indexed column appears bare on one side. Wrapping it in a function or arithmetic disables the index unless a matching expression index exists.
| Non-sargable | Sargable rewrite |
|---|---|
WHERE date(created_at) = '2024-01-01' | WHERE created_at >= '2024-01-01' AND created_at < '2024-01-02' |
WHERE lower(email) = $1 | keep it, but add an expression index ON t (lower(email)) |
WHERE col + 0 = $1 / WHERE col * 2 > $1 | move arithmetic to the constant side |
WHERE col LIKE '%foo%' | trigram GIN (pg_trgm) → azpg-index-selection |
WHERE col::text = $1 | compare in the column's native type |
Reading BUFFERS
shared hit = pages served from PostgreSQL's shared buffers (fast, in memory).
shared read = pages fetched from storage. On Flexible Server, read pages come
from managed remote storage, so a high read count with low hit is a strong
slowness signal — the node is I/O-bound. See
azure-constraints.md for why local timing intuition can
mislead on managed storage.
EXPLAIN options cheat-sheet
ANALYZE— actually run it, show real times/rows. Executes writes.BUFFERS— page hits/reads/dirtied; pair withANALYZE.FORMAT JSON— machine-readable, good for diffing plans.SETTINGS— show non-default planner GUCs in effect (PG12+).WAL— WAL volume for write statements (PG13+).VERBOSE— output columns and schema-qualified names.
Safety protocol
- Reads run automatically.
EXPLAINandEXPLAIN (ANALYZE, BUFFERS)on aSELECTchange nothing. ANALYZEexecutes the statement. ForINSERT/UPDATE/DELETE, never run a bareEXPLAIN ANALYZE. Use the rollback wrapper:BEGIN; EXPLAIN (ANALYZE, BUFFERS) UPDATE ...; ROLLBACK;scripts/explain_query.sqldoes exactly this.- Don't mutate to "fix" it here. This skill diagnoses. Index/config/DDL changes are handed to the write skills, which apply their own guarded, dry-run flow.
ANALYZE <table>(statistics refresh) is a safe, non-destructive maintenance command — recommend it freely when estimates are stale.
Bundled files
reference.md— every scan/join/aggregate/CTE node explained, the estimate-vs-actual method, and worked plan walkthroughs.azure-constraints.md— managed-storage timing, shared_buffers sizing by tier, remote-latency effects on plans.scripts/explain_query.sql— read-only; safe rollback wrapper forEXPLAIN ANALYZEon writes.scripts/find_slow_patterns.sql— read-only; surfaces tables with stale stats and high seq-scan ratios to explain why plans go wrong.examples/reading_a_plan.md— an annotated slow-plan walkthrough, from symptom to routed fix.