dynobase-icon
Dynobase

DynamoDB Joins: Modeling and Querying Related Data

Lakindu Hewawasam

Written by Lakindu Hewawasam

Last updated: October 23rd, 2026

Designing keys and access patterns? Model entities, keys and access patterns in Dynobase's Single-Table Designer, and exchange models with NoSQL Workbench. Try the Single-Table Designer

DynamoDB does not execute SQL JOIN operations, including through its PartiQL interface. You can read related entities together by modeling them in an item collection, combine separate reads in your application, or move data into an analytics engine that supports joins. A single-table Query returns stored items; your application still assembles the organization, users or other related objects into the response it needs.

Choose where the relationship is resolved

ApproachUseful whenMain tradeoff
Item collection in one tableRelated entities are commonly fetched togetherKey design must match the access pattern
Application-side joinEntities have independent storage or lifecycle requirementsMore reads and explicit consistency handling
Denormalized attributes or a read modelA stable combined view is read frequentlyDuplicated values need an update policy
Analytics engineQuestions require ad hoc joins and aggregationsData movement, freshness and analytics charges

Single-table design is one option, not a requirement for every application. Multiple tables can be appropriate when entities have different operational needs or are rarely read together. AWS's relational-data modeling example discusses grouping data around access patterns and using separate tables when appropriate.

Model an organization and its users

Keep the original example's relationship: an organization has many users, and each user belongs to one organization. The table is test-application, with String partition key PK and String sort key SK.

One-to-many relationship diagram showing an organization with multiple users

Store the organization profile and its users under the same partition-key value:

PKSKEntity typeExample attributes
ORG#APPLEORG#APPLEorganizationname: Apple
ORG#APPLEUSER#LAKINDUusername: Lakindu, role: admin
ORG#APPLEUSER#ALEXusername: Alex, role: member
ORG#MICROSOFTORG#MICROSOFTorganizationname: Microsoft

These are illustrative records, not claims about those companies' systems. In production, stable IDs are usually better key components than names because names can change. The ORG# and USER# prefixes are application conventions, not special DynamoDB syntax. Uniqueness comes from the combination of PK and SK.

The organization profile is an ordinary item whose sort key repeats its partition key. A user's sort key identifies that user within the organization. An entityType field is useful for application dispatch and inspection, but the query patterns below use keys rather than filtering on that field.

Define the model and access patterns

The following screenshots are from the original walkthrough and illustrate the key layout. They show an older editor interface; the textual schema and SDK v3 examples are the current instructions. Dynobase's data modeling workspace supports planning single-table access patterns and indexes.

Historical model editor defining String PK and SK attributes for the example table

Historical key-design view illustrating an ORG-prefixed partition key

Historical editor inserting organization profile records with matching PK and SK values

Historical editor adding a USER-prefixed item to an organization's item collection

Historical table view showing organization and user items stored together

The layout supports four distinct reads:

  1. Get the organization profile by its complete key.
  2. Query user items with the organization's PK and a USER# sort-key prefix.
  3. Get one user by the organization and user keys.
  4. Query the whole organization collection, including its profile and users.

Historical access-pattern definition for reading users within one organization

Historical access-pattern definition for reading one organization user

Historical result table showing the related item collections produced by the model

Sharing a partition-key value creates a logical item collection. It is not a promise that all of its data remains on one physical partition. Avoid making a correctness or latency assumption depend on physical co-location.

Querying the modeled data with SDK v3

Install @aws-sdk/client-dynamodb and @aws-sdk/lib-dynamodb, configure AWS credentials and a Region, and use an ES module. The examples assume the sample records already exist.

Querying organization information

When the whole primary key is known, GetItem is the direct lookup. The document client names this command GetCommand:

A Query with equality on both keys can retrieve the same item, but adds no benefit here. The result is one stored profile, not a profile joined with every user.

Historical query result containing the organization profile item

Fetching all users in an organization

Query the USER# sort-key prefix and paginate the results:

The sort-key condition selects user items before reading the result range. It does not return the organization profile because that item's SK begins with ORG#. Use Query vs Scan to understand why this key-based selection differs from filtering a table scan.

Historical result containing user items selected by the USER prefix

Fetching one user in an organization

Both key values are required. If a user moves to a different organization, its key changes, so model that as a coordinated delete and put rather than an update to the existing key.

Historical lookup result containing one user in the selected organization

Fetching the organization and its users

Omit the sort-key predicate to read the complete item collection:

This constructs a combined application response. It does not repeat the organization's columns on each user as a relational join might. The array accumulation is suitable only for a bounded collection; paginate users in the application when an organization can have many members.

Historical result showing organization and user records returned together

A Query request evaluates at most 1 MB before filtering. Even a well-designed collection can require several calls, so a single access pattern is not necessarily a single network request. The pagination guide covers streaming results and continuation cursors.

Application-side joins across tables

Suppose user profiles live in Users and organization memberships live in another table. First query the membership keys, deduplicate the referenced user IDs, then fetch the corresponding profiles and assemble the response by ID. Avoid issuing a separate unbounded sequence of reads for every membership.

BatchGetItem can retrieve known primary keys from multiple tables in one call. It accepts up to 100 items with a 16 MB response limit, may return partial results, and does not preserve requested order. Retry UnprocessedKeys with backoff, include key attributes in projections, and map returned items by their full keys. Missing records must be handled explicitly; they are omitted from the response. See the BatchGetItem reference.

BatchGetItem does not discover related keys and is not an atomic multi-item snapshot. Strong reads can improve individual read freshness, but they do not make the lookup and subsequent batch one transaction. If an invariant requires an atomic read of a known set of items, evaluate transactional reads and their constraints instead.

Many-to-many relationships and reverse lookups

If a user can belong to multiple organizations, store a membership item for each relationship. Keep canonical user details separately, or copy selected values onto memberships with an explicit synchronization policy.

For example, membership items can keep PK = ORG#123 and SK = USER#456. A GSI that uses SK as its partition key and PK as its sort key can read all memberships for USER#456. Define the projection so the index includes the fields that reverse lookup requires. A GSI is an additional stored index and consumes storage and write resources; it is not a free join over two attributes.

This is an adjacency-list approach, described in AWS's many-to-many modeling guide. It supports known edge lookups. Arbitrary multi-hop traversals can still need repeated reads and may fit a graph database better.

Denormalization, updates and consistency

Copying an organization's display name onto each membership can eliminate a second read, but a rename now affects multiple records. Decide whether the copied name is a historical snapshot or a current value. Those are different data requirements, not merely different update strategies.

For current copies, synchronous updates need a bounded write set and failure handling. An asynchronous read model can use change events to update copies, but consumers must tolerate retries and a period of staleness. GSIs are also eventually consistent, so a newly added membership may not immediately appear through the reverse index.

DynamoDB does not enforce foreign keys or cascading deletes. Define behavior for an organization deleted while memberships remain, a missing user profile, and partial failures during membership creation. Conditional writes and transactions can protect bounded invariants; they do not replace lifecycle design.

Performance and choosing an analytics engine

Reading one targeted item collection can avoid a series of application lookups. That is a useful reduction in round trips, not proof that DynamoDB is universally faster than an SQL database. Measure returned bytes, capacity consumed, pagination and hot-key behavior for the actual workload.

Start from the reads your application must serve. Design selective keys, bound collection growth, and use indexes for additional access patterns when their storage and write costs are justified. A filter that discards most candidates is a signal to revisit the model. A large organization concentrated behind one hot key can need a different distribution strategy, which changes how its results are gathered.

For reporting across arbitrary entities, export to S3 for Athena or use a suitable zero-ETL analytics integration. Those engines perform the join outside DynamoDB. Amazon EMR with Hive can also join mapped DynamoDB tables, as the AWS Hive examples demonstrate; reads against live source tables need a capacity budget. The DynamoDB SQL guide compares operational PartiQL with analytics options.

Frequently Asked Questions

Can DynamoDB do joins?

DynamoDB has no native SQL JOIN, including in its PartiQL interface. Model related items for a key-based read, combine reads in your application, or use an external analytics engine.

Can DynamoDB store relational data?

Yes. Relationships can be represented by item collections, membership records, embedded data or references. DynamoDB does not enforce foreign keys for those references.

Does single-table design return a joined result?

It can retrieve related stored items through one access pattern. Your application assembles their result shape, and large collections still require pagination.

How does DynamoDB handle many-to-many relationships?

An adjacency list stores relationship items under an entity key. An inverted GSI or a second set of edge items can support reverse lookups, with consistency and write costs to account for.

Can BatchGetItem join two tables?

It can fetch known primary keys from multiple tables, but it cannot discover relationships or execute a join predicate. Your application must map and combine the returned items and retry unprocessed keys.

Dynobase is a Professional GUI Client for DynamoDB

Start your 7-day free trial today

Product Features

Download
/
Changelog
/
Pricing
/
Member Portal
/
Privacy
/
EULA
/
Twitter
© 2026 Dynobase