Skip to content
DevOps AI ToolKit
Newsletter
All prompts
AI for Postgres Difficulty: Advanced ClaudeChatGPTCursor

Postgres Prepared Statement Generic vs Custom Plan Tuning Prompt

Diagnose why a parameterized/prepared query flipped to a bad generic plan after the sixth execution — and decide between plan_cache_mode, query rewrites, or letting Postgres re-plan — so latency stops spiking under skewed parameters.

Target user
Backend and platform engineers debugging prepared-statement plan regressions
Difficulty
Advanced
Tools
Claude, ChatGPT, Cursor

The prompt

You are a senior PostgreSQL DBA who understands the prepared-statement plan cache: Postgres
builds custom plans for the first five executions, then compares their average cost against a
generic plan, and may lock in the generic plan permanently. On columns with skewed data
distribution this can produce a plan that is great for a common parameter and terrible for a
rare one — a latency regression that appears only after the query "warms up."

I will paste:
- The prepared/parameterized query (or the ORM-generated SQL with $1, $2 params): [QUERY]
- Postgres version and relevant settings (plan_cache_mode if set): [VERSION / SETTINGS]
- Plans for the SAME query with a common param and with a skewed param, ideally from
  `EXPLAIN (ANALYZE, BUFFERS)` on a manually PREPAREd statement or with GENERIC_PLAN: [PLANS]
- Column statistics if known (n_distinct, most_common_vals, histogram): [STATS]
- How the query is issued (server-side prepare, pgbouncer pooling mode, ORM): [CLIENT]

Work through this in order:

1. **Confirm this is a generic-vs-custom-plan problem** — compare the two plans. A classic tell
   is a generic plan using a Seq Scan or the wrong index because the planner assumed an average
   selectivity, while the custom plan (which sees the literal value) picks a selective Index
   Scan. Note the estimate-vs-actual row gap on the skewed parameter.

2. **Name the root cause** — data skew on a bound column (a few values match millions of rows,
   most match a handful), stale or missing extended statistics, or a partitioned table where
   the generic plan cannot prune. Tie each to plan evidence.

3. **Propose fixes, ranked by impact and risk** — options include: force per-execution custom
   plans with `plan_cache_mode = force_custom_plan` (session or per-role, with the CPU cost of
   re-planning noted); improve statistics (`ALTER TABLE ... ALTER COLUMN ... SET STATISTICS`,
   `CREATE STATISTICS`, then `ANALYZE`); rewrite to avoid a single skewed predicate; or disable
   server-side prepare for that one query at the driver. Give the exact command for each.

4. **Give a verification step** — how to reproduce the flip (execute the prepared statement 6+
   times and re-EXPLAIN), and what should change after the fix (stable plan across parameters,
   estimate close to actual, no Seq Scan on the rare value).

Output format: (a) one-line verdict, (b) ranked fix table [change | scope | impact | risk], (c)
exact commands/settings in order, (d) how to verify the regression is gone.

Guardrails: prefer the narrowest scope — set plan_cache_mode per-session or per-role, not
cluster-wide, unless you have measured the CPU cost of forcing custom plans everywhere. Test on
a replica with production-like data skew; a fix that helps the rare parameter must not regress
the common one.

Run this prompt with AI

Test it, get an AI-improved version, or compare models — live in the Prompt Workspace. No copy-paste.

Why this prompt works

Generic-plan regressions are among the most confusing Postgres performance bugs because nothing changed in the query or the schema — the plan flipped on its own after the sixth execution when the plan cache decided the generic plan was “cheap enough.” Engineers who do not know this mechanism chase phantom causes. This prompt names the mechanism up front and forces a side-by-side comparison of the custom and generic plans, which is the only reliable way to confirm the diagnosis.

Anchoring on data skew is what makes it accurate. The generic plan is costed against average selectivity, so it is a bad fit precisely when the bound column has a lopsided distribution — a few values matching millions of rows and most matching a handful. By asking for n_distinct and most_common_vals, the prompt ties the plan choice to the statistics that actually drove it, rather than guessing.

The ranked fixes span the real toolbox — plan_cache_mode, extended statistics, query rewrites, and disabling server-side prepare at the driver — instead of jumping straight to the biggest hammer. The guardrails enforce the two things people get wrong: forcing custom plans cluster-wide (paying re-planning CPU on every query) and testing against uniform data that never reproduces the skew.

Related prompts

More Postgres prompts & error guides

Browse every Postgres prompt and troubleshooting guide in one place.

Free download · 368-page PDF

Reading prompts? Get all 500 in one free PDF

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.