The essentials

Quick reference

One focused task per row. Jump to the related section for complete, working examples.

UseSyntaxExamples
Prepare a typed queryPREPARE order_by_id (bigint) AS SELECT id, status, total FROM app.orders WHERE id = $1;View examples
Execute a statementEXECUTE order_by_id(42001);View examples
Prepare a writePREPARE add_event (uuid, text) AS INSERT INTO app.events(account_id, kind) VALUES ($1, $2) RETURNING id;View examples
Fix parameter meaningPREPARE events_since (timestamptz) AS SELECT id FROM app.events WHERE occurred_at >= $1::timestamptz;View examples
Inspect the chosen planEXPLAIN (ANALYZE, BUFFERS, SETTINGS) EXECUTE orders_for_tenant(42);View examples
Test custom planningSET LOCAL plan_cache_mode = 'force_custom_plan';View examples
Test generic planningSET LOCAL plan_cache_mode = 'force_generic_plan';View examples
Restore automatic choiceSET plan_cache_mode = 'auto';View examples
List prepared statementsSELECT name, parameter_types, generic_plans, custom_plans FROM pg_prepared_statements ORDER BY name;View examples
Find generic-plan usersSELECT name, generic_plans, custom_plans FROM pg_prepared_statements WHERE generic_plans > 0;View examples
Discard cached plansDISCARD PLANS;View examples
Release one statementDEALLOCATE order_by_id;View examples
Release all statementsDEALLOCATE ALL;View examples
Pin object resolutionPREPARE active_accounts AS SELECT id FROM app.accounts WHERE disabled_at IS NULL;View examples
Check server versionSHOW server_version;View examples
Identify two-phase stateSELECT gid, prepared, owner, database FROM pg_prepared_xacts ORDER BY prepared;View examples

Prepared statements separate parsing from repeated execution and can reduce planning work, but they are session-local state rather than a universal speed switch. Parameter-sensitive data distributions, connection pooling, DDL, statistics changes, privileges, and failover all affect their behavior. Measure both planning and execution, use typed value parameters, and let the driver own protocol-level statements unless an operational reason calls for SQL PREPARE.

Step by step

Detailed examples

01

Prepare once per session and execute with typed values

PREPARE accepts SELECT, INSERT, UPDATE, DELETE, MERGE, or VALUES. PostgreSQL parses, analyzes, and rewrites at preparation, then plans when EXECUTE needs a plan. Names are unique within one database session and disappear when that session ends. Explicit parameter types reduce inference surprises and make operator and index selection easier to reason about.

Prepare and reuse an account lookup
PREPARE order_by_id (bigint) AS
SELECT id, status, total
FROM app.orders
WHERE id = $1;

EXECUTE order_by_id(42001);
EXECUTE order_by_id(42002);
Back to quick reference ↑
02

Bind data values, not SQL structure

Parameters stand in for values; they cannot replace table names, column names, keywords, or sort directions. Use driver bind APIs for untrusted values and a strict allowlist when identifiers must vary. Parameterization prevents values from becoming SQL syntax, but it does not grant privileges or bypass row-level security. Keep pooled sessions from crossing trust boundaries with stale role, search_path, or prepared-statement state.

Parameterize an insert and return its identifier
PREPARE add_event (uuid, text) AS
INSERT INTO app.events(account_id, kind)
VALUES ($1, $2)
RETURNING id;

EXECUTE add_event('7cc8f02b-c7f4-47cc-9532-498908014e1b', 'invoice.sent');

Note: For application code, use the driver's parameter API rather than constructing this EXECUTE call from untrusted text.

Back to quick reference ↑
03

Diagnose generic plans against parameter skew

A custom plan uses current parameter values; a generic plan can be reused across calls. With plan_cache_mode=auto, PostgreSQL currently runs the first five parameterized executions with custom plans, then compares their average estimated cost with a generic plan. Highly skewed tenants or predicates can make one generic plan poor for outliers. Compare realistic values with EXPLAIN; EXPLAIN ANALYZE executes the statement and can write data for DML, so wrap tests safely or omit ANALYZE.

Compare both planning modes for one read
BEGIN;
SET LOCAL plan_cache_mode = 'force_custom_plan';
EXPLAIN (ANALYZE, BUFFERS, SETTINGS)
EXECUTE orders_for_tenant(42);

SET LOCAL plan_cache_mode = 'force_generic_plan';
EXPLAIN (ANALYZE, BUFFERS, SETTINGS)
EXECUTE orders_for_tenant(42);
ROLLBACK;

Note: Run performance experiments on a safe workload replica or controlled environment; ANALYZE really executes the statement.

Back to quick reference ↑
04

Inspect the cache from the owning connection

pg_prepared_statements exposes only prepared statements available in the current session, including whether SQL PREPARE or the frontend/backend protocol created them. generic_plans and custom_plans are cumulative choice counts, not timing data. Capture EXPLAIN results and workload latency separately, and remember that connecting through another pool backend gives a different view.

Review statement identity and plan counts
SELECT name, prepare_time, parameter_types, result_types, from_sql,
       generic_plans, custom_plans
FROM pg_prepared_statements
ORDER BY prepare_time;
Back to quick reference ↑
05

Let PostgreSQL invalidate plans and retire unused state

PostgreSQL re-analyzes and replans after relevant object-definition changes, planner-statistics updates, or search_path changes. DISCARD PLANS releases cached plans but keeps the prepared statements; DEALLOCATE removes statements. DISCARD ALL is a broader session reset, includes DEALLOCATE ALL and advisory-lock cleanup, and cannot run inside a transaction block. Coordinate manual cleanup with the driver because it may cache server statement names too.

Choose the narrowest cleanup operation
DISCARD PLANS;             -- keep definitions; force later replanning
DEALLOCATE order_by_id;    -- remove one SQL prepared statement
DEALLOCATE ALL;            -- remove all SQL prepared statements
Back to quick reference ↑
06

Make deployment boundaries predictable

Schema-qualify application objects and list result columns explicitly. Although search_path changes trigger reparsing, creating a same-named object earlier on an unchanged path does not itself invalidate an already resolved statement. DDL that changes a protocol-prepared query's result shape can also surprise clients that cached row metadata. Roll out compatible schemas before application code, drain or recycle long-lived connections when necessary, and test old and new clients together.

Use a stable, explicit result contract
PREPARE active_accounts AS
SELECT a.id, a.display_name, a.created_at
FROM app.accounts AS a
WHERE a.disabled_at IS NULL
ORDER BY a.id;
Back to quick reference ↑
07

Treat prepared state as connection-local and disposable

Drivers commonly use the extended query protocol and may decide when to name and cache statements; SQL PREPARE is not a substitute for the driver's bind API. Transaction-pooling proxies can hand consecutive transactions to different server sessions, so named session state requires explicit pooler and driver support. A reconnect, server restart, or failover loses prepared statements; recreate them or let the driver do so. They are neither durable nor WAL-replicated. PREPARE TRANSACTION is separate two-phase-commit state and should not be confused with query preparation. Verify behavior against the deployed PostgreSQL and client versions.

Distinguish statements from two-phase transactions
SELECT name, prepare_time
FROM pg_prepared_statements
ORDER BY prepare_time;

SELECT gid, prepared, owner, database
FROM pg_prepared_xacts
ORDER BY prepared;

Note: The first view is session-local query state; the second is durable two-phase transaction state visible cluster-wide. pg_prepared_statements.result_types requires PostgreSQL 16 or newer.

Back to quick reference ↑

Sources and further reading

References

Authoritative documentation used to verify and expand this cheat sheet.

  1. PostgreSQL Global Development GroupPostgreSQL: PREPAREpostgresql.org
  2. PostgreSQL Global Development GroupPostgreSQL: EXECUTEpostgresql.org
  3. PostgreSQL Global Development GroupPostgreSQL: pg_prepared_statementspostgresql.org
  4. PostgreSQL Global Development GroupPostgreSQL: Query Planning Configurationpostgresql.org
  5. PostgreSQL Global Development GroupPostgreSQL: Frontend/Backend Protocol Overviewpostgresql.org
  6. PostgreSQL Global Development GroupPostgreSQL: DISCARDpostgresql.org

Help us improve

Found a typo or missing example?

Tell us what would make this cheat sheet clearer, more complete, or more useful.

Share feedback