liveBook · Manning

Antipattern

As explained in PostgreSQL Mistakes and How to Avoid Them

An antipattern is a recurring design or implementation approach that appears reasonable but produces harmful consequences.

An antipattern is a recurring choice that appears reasonable but creates harmful consequences. In PostgreSQL, mistakes may remain acceptable at small scale yet become costly, risky, or difficult to correct in production. section 1.2

Relational JSON is one PostgreSQL antipattern: storing strongly relational data entirely inside JSON fields. JSON is valuable for unforeseeable attributes and ingestion, but becomes harmful when ordinary relational data requires joins, casts, and application-enforced rules. section 5.3

Recognize the antipattern when:

  • A design uses JSON for data that fits a relational pattern well. section 5.3
  • Queries repeatedly extract JSON values, cast them to scalar types, and join on them. section 5.3
  • The database cannot enforce referential integrity or reliably ensure value types inside the stored documents. section 5.3
  • A flexible schema creates difficult queries that are easy to write incorrectly or make perform badly. section 5.3

Relational JSON in customer and sales data

Accounts and sales are stored as one jsonb value per row. Retrieving customer names for qualifying sales requires extracting identifiers, casting them, joining JSON fields, comparing numeric values, and inspecting nested migration data. The design maximizes flexibility at the cost of relational safeguards and query simplicity. section 5.3

CREATE TABLE test.accounts (
    json_account jsonb NOT NULL
);

CREATE TABLE test.sales (
    json_sale jsonb NOT NULL
);

Shows the accounts and sales tables whose complete rows are stored in jsonb columns. section 5.3

SELECT a.json_account ->> 'name' AS "Name"
FROM test.accounts a
JOIN test.sales s
    ON (s.json_sale ->> 'account_id')::int = (a.json_account ->> 'id')::int
WHERE (a.json_account ->> 'balance')::numeric < 20000
AND (s.json_sale ->> 'amount')::numeric > 10000
AND (NOT a.json_account['migrated_account'] ->> 'system' = 'legacy'
    OR a.json_account -> 'migrated_account' IS NULL);

Shows the extraction, casting, join, filtering, and nested-field checks required by the JSON design. section 5.3

How PostgreSQL Mistakes and How to Avoid Them covers it

How deep it goes
Explains relational JSON through storage examples and query consequences, but does not provide a complete replacement schema.
Examples
Uses jsonb account and sales tables, nested documents, and a join-heavy query with casts and JSON operators.
What it assumes
Assumes database experience while grounding the distinction between relational modeling, EAV, JSON storage, and SQL joins.
What you can do after
Evaluate whether JSON storage fits unforeseeable attributes or improperly replaces relational structure.
How it connects
Connects antipatterns with PostgreSQL, JSON, JSONB, NoSQL, EAV, referential integrity, data types, and SQL joins.

Also explained in

Compare how 5 books explain antipattern →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of PostgreSQL Mistakes and How to Avoid Them. The section links point to the parts of the book it draws on.