The Document Model against Rows

The same data as normalised tables and as nested documents, and what each shape makes easy.

Overview

The same order, two shapes

Run the query above and then the variants. The identical order is stored as normalised rows and as a single JSON document, and both are queried in the same session — because modern SQL engines support both.

Relational: an orders row, several order_items rows referring back to it, a customers row. Each fact recorded once, joined when needed.

Document: one record containing the order, its items nested inside it, and the customer details copied in.

The Document Model against Rows

The same data, both ways

query.sql SQLite
Result

Worth knowing

A document stores related data together, nested. A relational schema stores it apart and joins on demand.
Documents win when the access pattern is 'give me this whole thing' — one read, no joins.
Rows win when the data is queried from several directions, or when the same fact appears in many documents and has to stay consistent.
SQLite, PostgreSQL and MySQL all have JSON columns, so the choice is per-column now rather than per-database.

The Document Model against Rows

One order, stored two ways, and an honest account of which questions each shape answers well.

What documents make easy

One read for one thing. Fetching an order means retrieving one document. No joins, no round trips, and the data is contiguous on disk. For a read-this-whole-object access pattern, this is genuinely faster and the gap widens as the object gets more parts.

A shape that matches the code. The document deserialises straight into an object. No object-relational mapping layer reassembling a graph from five result sets.

Schema flexibility. Adding a field to some documents and not others requires no migration. For genuinely heterogeneous data — product catalogues where a book and a fridge share almost no attributes — this is a real advantage rather than laziness.

What rows make easy

Querying from any direction. "Which customers bought product 902" is straightforward relationally and awkward in a document store, because the data is organised around orders, not products. Documents optimise one access path and make the others harder.

Updating a shared fact once. If the customer's address is copied into every order document, changing it means finding and rewriting every one. Relationally it is a single UPDATE. This is the classic normalisation argument and it has not stopped being true.

Integrity the database enforces. Foreign keys, uniqueness and check constraints are declared once and cannot be bypassed. In a document store these usually become application code, which means they hold until some other code path forgets.

Ad-hoc analysis. Aggregating across documents means either a scan or a purpose-built index, and the queries are harder to write.

Denormalisation is the actual trade

Nesting the items inside the order is fine — an order item belongs to exactly one order and is never queried independently. That is not duplication, just co-location.

Copying the customer's name and address into every order is duplication, and it buys read speed at the cost of update cost and the risk of divergence. Sometimes that is right: an invoice arguably *should* record the address as it was at the time, in which case it is not duplication at all but a historical fact.

The question is always whether the copies must agree. If they must, storing them separately means keeping them in sync forever.

The distinction has mostly dissolved

PostgreSQL's jsonb is indexable, queryable and transactional. MySQL and SQLite have JSON functions — the queries on this page use SQLite's, in the same session as ordinary tables.

So the modern answer is usually neither purely one nor the other: relational tables for the entities that are queried from several directions and must stay consistent, and a JSON column for the parts that are genuinely variable and only ever read alongside their parent.

What a dedicated document database still offers is horizontal scaling and operational tooling built around that model from the start. What it gives up is joins and multi-document transactions — though MongoDB has had the latter since 4.0, which narrowed the gap considerably.

Where it goes wrong

Choosing documents to avoid schema design. The schema still exists; it has moved into the application, where nothing enforces it.

Unbounded arrays. A document that grows without limit — every event appended to one record — eventually exceeds the size limit and rewrites the whole thing on every append.

Duplicating a fact that must stay consistent. Fine for a historical snapshot, a slow disaster for live data.

Using JSON columns for well-structured data. If every row has the same fields, they are columns. Putting them in JSON gives up type checking and constraints for nothing.

Check yourself

0 of 3

Answer without scrolling back up.

  1. When does the document model genuinely beat normalised rows?

  2. What is the real cost of copying a customer's address into every order document?

  3. Why has the relational/document distinction largely dissolved?

Cheat sheet

The Document Model against Rows

Run the query above and then the variants. The identical order is stored as normalised rows and as a single JSON document, and both are queried in the same session — because modern SQL engines support both.

DATABASE · vizlearn.in/database/document_model_vs_rows.html

About the author

Ashish Jangra builds and maintains VizLearn. Every module here is written and the visualisation behind it hand-built, so the numbers in a readout come from the same code that draws the picture. Corrections are genuinely welcome and get priority over everything else — if a page states something wrong, or an animation misrepresents what the algorithm does, get in touch.