DynamoDB Query vs Scan

Once your data is in DynamoDB, you need to retrieve it. Two API operations handle bulk data retrieval: Query and Scan. They look similar on the surface but work very differently. Using the wrong one can cost you significantly more money and slow your application.

The Core Difference

Think of a large library. A Query is like going directly to the shelf labeled "Cooking," picking up exactly the books you want. A Scan is like walking every single aisle in the library, picking up every book, reading its cover, and then keeping only the cooking books.

Query targets a specific partition. Scan reads every item in the entire table.

Query

A Query retrieves all items that share the same partition key value. You must always provide the partition key. Optionally, you can filter on the sort key to narrow results within that partition.

Example: Fetch All Orders for a User

Table: Orders — Partition Key: UserID, Sort Key: OrderDate

Query:
  Partition Key = "U001"
  
Result: All 3 orders for User U001 — retrieved from ONE partition
┌──────────┬────────────┬──────────┐
│ UserID   │ OrderDate  │ Product  │
├──────────┼────────────┼──────────┤
│ U001     │ 2024-01-05 │ Laptop   │
│ U001     │ 2024-03-12 │ Mouse    │
│ U001     │ 2024-06-20 │ Monitor  │
└──────────┴────────────┴──────────┘

DynamoDB went directly to the U001 partition and returned three items. It did not look at any other partition.

Query with Sort Key Condition

Add a sort key condition to narrow the results:

Query:
  Partition Key = "U001"
  Sort Key BETWEEN "2024-01-01" AND "2024-04-30"

Result: Orders only in the first four months of 2024
┌──────────┬────────────┬──────────┐
│ UserID   │ OrderDate  │ Product  │
├──────────┼────────────┼──────────┤
│ U001     │ 2024-01-05 │ Laptop   │
│ U001     │ 2024-03-12 │ Mouse    │
└──────────┴────────────┴──────────┘

Sort Key Operators for Query

OperatorMeaningExample
=Exact matchOrderDate = "2024-03-12"
<Less thanOrderDate < "2024-06-01"
<=Less than or equalOrderDate <= "2024-06-01"
>Greater thanOrderDate > "2024-01-01"
>=Greater than or equalOrderDate >= "2024-01-01"
BETWEENWithin a range (inclusive)OrderDate BETWEEN "2024-01" AND "2024-04"
begins_withStarts with a prefixOrderDate begins_with "2024"

Query Results Are Sorted

Query results come back in sort key order by default. Use ScanIndexForward: false to get them in reverse order (newest first).

Scan

Scan reads every item in the entire table (or index) and optionally filters the results. You use it when you do not have a partition key to start from.

Example: Find All Orders Over ₹50,000

Scan with FilterExpression: Amount > 50000

Step 1: DynamoDB reads ALL items in the Orders table
Step 2: DynamoDB applies the filter
Step 3: Returns only items where Amount > 50000

DynamoDB reads 10,000 items → returns 23 items
You pay for reading all 10,000 items.

This is why Scan is expensive. Even if only 23 items match, you are charged for reading all 10,000.

Diagram: Query vs Scan Efficiency

Table: Orders (10,000 items across 5 partitions)

QUERY (UserID = "U001"):
  Partition A [U001 items] ← Only reads this partition
  Partition B [U003 items]    (not touched)
  Partition C [U007 items]    (not touched)
  Partition D [U002 items]    (not touched)
  Partition E [U009 items]    (not touched)
  
  Reads: ~50 items (U001's orders only)
  Cost: Low

SCAN:
  Partition A ←── reads all
  Partition B ←── reads all
  Partition C ←── reads all
  Partition D ←── reads all
  Partition E ←── reads all
  
  Reads: 10,000 items
  Cost: Very high

Parallel Scan

For large tables, Scan supports parallel execution across multiple workers. Each worker scans a separate segment of the table simultaneously. This reduces the total time but increases cost because you still read every item.

Parallel Scan with 4 workers:
Worker 1 → Segment 0 (25% of table)
Worker 2 → Segment 1 (25% of table)
Worker 3 → Segment 2 (25% of table)
Worker 4 → Segment 3 (25% of table)
→ All four run at the same time
→ Total time ≈ 1/4 of a single-threaded scan

Pagination

Both Query and Scan return a maximum of 1 MB of data per call. If more data exists, the response includes a LastEvaluatedKey. Pass this key into the next request to continue reading from where the previous call stopped. This is called pagination.

Request 1 → returns items 1–100 + LastEvaluatedKey
Request 2 (using LastEvaluatedKey) → returns items 101–200 + LastEvaluatedKey
Request 3 (using LastEvaluatedKey) → returns items 201–247 + no LastEvaluatedKey (done)

When to Use Query vs Scan

SituationUse
Retrieve all orders for one userQuery
Retrieve orders for a user between two datesQuery
Get a list of all users (admin report)Scan (with caution)
Find all products with price over ₹10,000Scan or GSI-based Query
Load test data in bulkScan (acceptable for one-time tasks)

Best Practices

  • Design your primary keys to enable Queries for all critical access patterns.
  • Use Global Secondary Indexes (GSIs) to enable Queries on non-key attributes instead of relying on Scan.
  • Avoid Scan on large production tables — it is slow, expensive, and can consume all your read capacity.
  • If you must use Scan, apply a Filter Expression to reduce returned data and add a rate limit to avoid consuming all your capacity.

Leave a Comment

Your email address will not be published. Required fields are marked *