What is the difference between Query and Scan operations in DynamoDB?
Understand DynamoDB Query vs Scan: how each reads data, why filters do not cut Scan cost, capacity trade-offs, and when to use each — with code examples.
Expected Interview Answer
Query retrieves items by matching a specific partition key (and optional sort key condition), reading only the relevant items, while Scan reads every item in the entire table or index and then applies any filter afterward. Query is targeted and efficient; Scan is a full sweep and should be avoided for large tables.
A Query must specify an equality condition on the partition key and can add a sort-key condition, so DynamoDB reads only that partition's item collection and consumes capacity proportional to the items returned. A Scan examines all items across all partitions, consuming capacity for everything it reads even if a FilterExpression discards most of them — filters are applied after the read, so they do not reduce cost. Scan can be parallelized with segments and paginated, but for predictable performance you design keys and GSIs so access patterns are served by Query, reserving Scan for rare full-table jobs like exports or migrations.
- Query reads only the matching partition, so it is fast and cheap
- Query supports sort-key conditions for ranges and prefixes
- Scan can traverse an entire table when no key pattern fits
- Parallel Scan segments speed up unavoidable full sweeps
- Both support pagination, projection and consistent reads
AI Mentor Explanation
Query is asking the scorer for every ball bowled by one specific bowler — they flip straight to that bowler's spell and read it off. Scan is re-reading the entire match ball by ball just to find those deliveries, then discarding the rest. Both give you the bowler's figures, but one goes directly to the right page while the other combs the whole scorebook, costing far more effort for the same answer.
Step-by-Step Explanation
Step 1
Decide by access pattern
If you know the partition key you want, use Query; only fall back to Scan when no key or index matches the pattern.
Step 2
Build a Query key condition
Specify partition key equality plus an optional sort-key condition (=, <, between, begins_with) in KeyConditionExpression.
Step 3
Understand filter timing
FilterExpression on either operation runs AFTER items are read, so it reduces returned data but not consumed read capacity.
Step 4
Use GSIs to avoid Scans
If a needed pattern lacks a matching key, add a Global Secondary Index so it becomes a Query instead of a Scan.
Step 5
Parallelize unavoidable Scans
For genuine full-table jobs, use Segment and TotalSegments to run parallel Scan workers, and paginate with LastEvaluatedKey.
What Interviewer Expects
- Query requires a partition key; Scan reads the whole table
- Capacity cost difference and why filters do not reduce it
- KeyConditionExpression versus FilterExpression semantics
- Using GSIs to convert Scans into Queries
- When a Scan is legitimately appropriate (exports, small tables)
Common Mistakes
- Believing a FilterExpression lowers read capacity consumed
- Using Scan for routine access patterns on large tables
- Thinking Query can run without a partition key condition
- Ignoring pagination and LastEvaluatedKey for large result sets
- Not adding a GSI when the access pattern needs a different key
Best Answer (HR Friendly)
“Query goes straight to the exact group of records you ask for using its key, so it is fast and cheap. Scan reads the entire table and then throws away what you did not want, which is slow and costly on big tables, so it is used only for occasional full sweeps like exports.”
Code Example
import { QueryCommand, ScanCommand } from '@aws-sdk/lib-dynamodb'
// Query: reads ONLY the CUSTOMER#1 partition — fast, cheap
const q = await ddb.send(new QueryCommand({
TableName: 'AppTable',
KeyConditionExpression: 'PK = :pk AND begins_with(SK, :o)',
ExpressionAttributeValues: { ':pk': 'CUSTOMER#1', ':o': 'ORDER#' },
}))
// Scan: reads EVERY item, filter applied AFTER the read (still billed)
const s = await ddb.send(new ScanCommand({
TableName: 'AppTable',
FilterExpression: '#t = :t',
ExpressionAttributeNames: { '#t': 'total' },
ExpressionAttributeValues: { ':t': 90 },
}))Follow-up Questions
- Why does a FilterExpression not reduce consumed read capacity?
- How does parallel Scan with segments work?
- When is a Scan actually the right choice?
- How do you paginate results using LastEvaluatedKey?
- How can adding a GSI turn a Scan into a Query?
MCQ Practice
1. What must a DynamoDB Query always specify?
Query requires the partition key value; it reads only that partition's item collection, optionally narrowed by a sort-key condition.
2. When is a FilterExpression applied during a Scan?
Filters run after DynamoDB reads the items, so they cut the returned data but not the read capacity you are billed for.
3. How can you avoid a Scan for a new access pattern?
A GSI provides an alternate key, letting the pattern be served by an efficient Query instead of a full-table Scan.
Flash Cards
Query vs Scan in one line? — Query targets one partition by key; Scan reads the whole table and filters afterward.
Do filters reduce Scan cost? — No — FilterExpression runs after the read, so you are billed for every item read regardless of matches.
What does a Query require? — An equality condition on the partition key, optionally narrowed by a sort-key condition.
When is Scan appropriate? — Rare full-table jobs like exports, migrations, or analytics on small tables where no key pattern fits.