Overview

Appendix B. Cheat sheet

This cheat sheet distills the most common PostgreSQL pitfalls and the practices that prevent them. It urges readers to align design choices, query habits, and operational routines with their actual needs, to favor clarity and correctness over convenience, and to plan proactively rather than react under pressure. The overarching themes are to choose appropriate data types and features, write predictable and efficient SQL, tune and observe the system continuously, and rely on proven tools and processes for safety, performance, and reliability.

On the SQL and schema front, it cautions against constructs with surprising semantics (for example, NOT IN with NULLs or BETWEEN’s inclusive bounds), reminds that integer division truncates, and that count on a nullable column skips NULLs. It warns that quoting identifiers creates case sensitivity and that selecting or fetching more than necessary harms performance. Use CTEs to improve readability and let the optimizer help, and be wary of expressions that prevent index use. Prefer clear designs over nullable columns in composite unique keys; choose TIMESTAMPTZ over timestamp without time zone; avoid MONEY; favor identity columns over SERIAL; prefer TEXT to fixed-length character types; use JSONB instead of XML; and think carefully before choosing UUIDs over integer keys. For tables and indexes, avoid inheritance in favor of foreign keys and triggers, use partitioning judiciously (with care around multi-key strategies), and match index types to data and queries to unlock orders-of-magnitude speedups. Treat SQL_ASCII as a non-choice, avoid RULEs in favor of TRIGGERs, and don’t mix document patterns with relational access if it complicates or slows queries.

Operational guidance centers on performance, safety, and discipline. Defaults are conservative, so tune memory and concurrency with workload in mind; too many sessions, idle connections, idle-in-transaction, or excessive transaction rates can throttle performance, block work, or even risk XID wraparound. Autovacuum is essential; use EXPLAIN to understand slow plans; index for your WHERE clauses but prune unneeded indexes carefully, especially in replicated environments. Administratively, watch disk usage, separate and rotate logs, monitor continuously, and retain long-term stats; keep PostgreSQL, the OS, and libraries up to date. Secure access and privileges tightly: avoid blanket listen_addresses, trust in pg_hba, superuser-owned databases, unsafe SECURITY DEFINER usage, and an unguarded search_path. For availability, backups beat snapshots; combine base backups with point-in-time recovery, automate and test restores, and use proven HA tooling rather than DIY failover. For upgrades and migrations, read all relevant release notes, test with realistic data and workloads, and handle encodings, booleans, and type differences explicitly. Above all, be proactive: review code and usage patterns, document and automate, and focus first on the biggest pain points to build robust, reliable systems.

FAQ

Why is NOT IN risky when the list can contain NULL values?Because a single NULL in the list makes the comparison evaluate to unknown for every row, returning no results. Use NOT EXISTS (anti-join) instead, or a LEFT JOIN ... IS NULL pattern.
How do I avoid overlapping results when filtering timestamp ranges with BETWEEN?BETWEEN is inclusive on both ends, so ranges can overlap. Use half-open intervals: WHERE ts >= start AND ts < end. This pattern avoids overlap when running consecutive queries.
Why does dividing integers return a truncated result?When both operands are integers, PostgreSQL performs integer division and truncates the result. Cast at least one operand to numeric (or double precision): amount::numeric / count.
Why doesn’t COUNT(column) return the number of rows?COUNT(column) ignores NULLs and returns the number of non-NULL values. To count rows, use COUNT(*) or COUNT(1). If you must count non-NULL after transformation, COALESCE first.
Should I quote identifiers (table/column names) in PostgreSQL?Avoid it unless necessary. Quoted identifiers become case-sensitive and hurt usability. Follow the Postgres convention: unquoted, lower-case identifiers.
Which timestamp type should I use: TIMESTAMP WITHOUT TIME ZONE, TIMETZ, or TIMESTAMPTZ?Prefer TIMESTAMPTZ. It represents an absolute point in time and handles time zones/DST correctly. TIMESTAMP WITHOUT TIME ZONE and TIMETZ lack essential context and can cause mistakes.
Is the MONEY type a good choice for storing currency values?No. MONEY doesn’t store currency and has limitations. Use NUMERIC with an appropriate scale, and store the currency code in a separate column.
Are SERIAL and BIGSERIAL still recommended for auto-incrementing keys?Prefer identity columns: GENERATED {ALWAYS|BY DEFAULT} AS IDENTITY. They have clearer ownership/sequence semantics and are the modern replacement for SERIAL/BIGSERIAL.
Why might an index not be used when I apply a function in the WHERE clause?If you transform a column in the predicate (e.g., lower(email)), a plain index on the column may not be used. Create an expression index matching the predicate (CREATE INDEX ... ON t (lower(email))) or use a generated column.
Can too many (often idle) connections or excessive concurrency hurt performance?Yes. Each connection consumes resources; excessive concurrency increases contention and context switching. Use a connection pooler (e.g., PgBouncer in transaction mode), cap max_connections, and tune workloads for sensible concurrency.

pro $24.99 per month

  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose one free eBook per month to keep
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime

lite $19.99 per month

  • access to all Manning books, including MEAPs!

team

5, 10 or 20 seats+ for your team - learn more


choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • PostgreSQL Mistakes and How to Avoid Them ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • PostgreSQL Mistakes and How to Avoid Them ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • PostgreSQL Mistakes and How to Avoid Them ebook for free