Intermediate6 min read

When NOT to Use Single-Table Design in DynamoDB

Single-table design is the default advice for DynamoDB, and it earns it: one Query hands back a parent and its children, no joins, no N+1.

But it is a trade — you buy read speed with a rigid, opaque schema. Some workloads can't afford that price, and forcing one table on them is its own footgun.

When should you not use single-table design in DynamoDB?

Avoid single-table design when your workload is heavy analytics, plain CRUD over a handful of unrelated entities, or entities that scale and fail independently. In those cases multiple tables read better, cost the same, and stay flexible. Single-table design only wins when access patterns are known, related, and high-volume.

  • Heavy analytics? Don't single-table. Overloaded keys are -hostile — export to a columnar store and query there instead.
  • Plain CRUD with a handful of access patterns? One table per entity is fine, readable, and costs you nothing in performance.
  • Entities that scale or fail independently? Separate tables let you tune, bill, and blast-radius them on their own.
  • Single-table still wins when your patterns are known, related, and high-volume — that's the case it was built for.

Know what single-table actually costs

Single-table design isn't free; it just moves the cost off the read path and onto everything else. You pay in legibility and flexibility.

A table holding five entity types behind PK/SK is hard to read, hard to onboard onto, and hard to change. A new access pattern can mean a backfill across every item type in the partition.

So the question isn't "is single-table good?" It's "do my access patterns justify the rigidity?" When they don't, reach for multiple tables.

Don't single-table an analytics workload

DynamoDB is built for — small, known, point-and-range reads. Analytics is : GROUP BY, big aggregates, ad-hoc slicing across the whole dataset. The two pull in opposite directions.

Single-table design makes worse, not better. Overloaded keys and mixed entity types mean an analytics job must first untangle which item is which before it can sum anything — the opposite of a clean columnar scan.

Coming from SQL, the reflex is to write the aggregate against the live table. In DynamoDB that's a full Scan — you pay for and read every item, which is the Scan footgun at full volume.

The fix isn't a cleverer key. It's a different store. AWS's own guidance is to export DynamoDB to S3 and run analytics with a query engine like Athena.

Keep and on separate engines (AWS DynamoDB Developer Guide, S3DataExport.html).

Don't single-table simple CRUD

If your app reads and writes a few unrelated entities by their own id, and you have maybe three access patterns, single-table buys you nothing.

Take a Tenants table and an ApiKeys table for a small B2B tool:

Tenants
TenantIdNamePlanTier
TNT-8842Northwindpro
ApiKeys
KeyIdTenantIdScope
KEY-01H9...TNT-8842read-write

Each query is a GetItem by id, or a Query on a keyed by TenantId. There's no parent-and-children fetch to optimize, so there's nothing for an overloaded partition to win. Two clear tables read better than one murky one.

The trap is cargo-culting single-table because it's "best practice." Best practice is access-pattern-first. If the patterns are trivial, the simple shape is the right shape.

Split tables that scale or fail independently

A single table shares one throughput surface, one backup, one blast radius. When two entities have wildly different write rates or durability needs, that shared fate becomes a liability.

Picture a fleet-tracking system. Vehicles change rarely; their telemetry pours in every second:

Vehicles (cold, low write rate)
VehicleIdMakeModelRegion
VEH-204VolvoFH16eu-west
Telemetry (hot, high write rate, TTL'd)
DeviceTsVehicleIdSpeedKphFuel
2026-06-23T10:00:01ZVEH-204880.61

Two tables let you provision telemetry for a firehose, keep vehicles tiny and cheap, set a on telemetry alone, and stop a telemetry write storm from throttling reads of the vehicle catalog. One table couples all of that.

Per the 2007 Amazon Dynamo paper, partitioning and availability are first-class concerns — independent tables give you independent control over both.

Map the decision before you commit

Walk the workload through one gate: are the entities related, and are the patterns known and high-volume? If not, multiple tables.

Here's the call as a flow — start at the top and follow the first branch that matches:

YesNoNoYesNoYesNew workloadHeavy analyticsor OLAP?Separate store(export + Athena)Entities related+ fetched together?Multiple tables(simple CRUD)Known,high-volumepatterns?Multiple tables(stay flexible)Single-tabledesign

Only the bottom-right leaf earns single-table; every other path is better served by more than one table.

Single vs multiple, head to head

FactorSingle-tableMultiple tables
Related readsOne Query, no joinsClient-side join or extra round-trips
ReadabilityOpaque overloaded keysOne entity per table, self-documenting
New access patternOften a backfillAdd a table or GSI in isolation
Analytics / OLAPHostile — untangle before aggregatingStill export, but cleaner per-entity
Independent scalingShared throughput + blast radiusTune, bill, TTL, and back up separately
Best whenKnown, related, high-volume patternsUnrelated, evolving, or analytical

Pitfalls + next steps

The mirror-image mistake is over-correcting — splitting genuinely related, high-volume entities into a table-per-entity and rebuilding SQL-style joins in your app. That's the N+1 trap single-table kills.

Pick the shape the access patterns ask for, not the dogma.

When you do model relationships, lean on the right index type — see GSI vs LSI before you add one.

When you do write a query against a multi-table schema, sketch the KeyConditionExpression in the DynamoDB Expression Builder first.

That way you catch a full-Scan shape before it hits production.

Then try DynoTable to browse both shapes against your own tables and see which one your access patterns actually want.

Updated