Grain and Fan Traps: Advanced
What you will be able to do
"What Is the Grain of This Table?"
- ▸"Design a fact table for..."
- ▸"How would you model this data?"
- ▸"What would this table look like?"
- ▸"The numbers don't add up when we join these tables"
- ▸"We need to track X at the Y level"
- ▸Any question where the interviewer describes a business process
What They're Really Testing
The interviewer is not checking whether you know the word 'grain.' They are checking whether you instinctively open with it before doing anything else. The hidden rubric item is: does this candidate define the unit of analysis before designing the schema? If you skip this step and jump to column names, you have signaled that you build tables by feel rather than by discipline.
The 60-Second Framework
Step 3 is the strong-hire signal. Asking the interviewer a clarifying question about grain shows you have been burned by wrong assumptions in production. Junior candidates never ask. Senior candidates always do.
Grain Statements: Good vs Bad
- "The table has order_id, customer_id, product_id..."
- "Each row is an order"
- "It stores order data"
- "We'd put all the order info in one table"
- "The grain is one row per order line item"
- "Unique on (order_id, line_item_seq)"
- "Before I add columns, is this order-level or item-level?"
- "If a line item can be partially shipped, the grain might need to include shipment_id"
Why Companies Care
Defining Grain: One Row Represents What?
How Interviewers Grade Your Grain Statement
| Precision Level | Grain Statement | Verdict |
|---|---|---|
| Vague | "It's an orders table" | No hire. What about orders? |
| Better | "One row per order" | Hire if followed up. But what's an order? |
| Good | "One row per order line item" | Hire. Shows item-level thinking. |
| Strong Hire | "One row per order line item per fulfillment event" | Handles partial shipments. |
| Senior+ | "...unique on (order_id, line_seq, fulfillment_id), with a NOT NULL constraint on the composite" | Production-grade design. |
The jump from 'good' to 'strong hire' is acknowledging that a line item can have multiple lifecycle events. The jump from 'strong hire' to 'senior+' is naming the enforcement mechanism, not just the concept.
The Validation Move That Signals Production Experience
A grain statement without a validation strategy is just a comment. In production, grain is enforced by uniqueness constraints or dbt tests. In an interview, mentioning either one unprompted is a strong signal.
If this returns rows, your grain is violated. In dbt, this is a unique test on the composite key. Mentioning this in an interview shows you have operated models in production, not just designed them on whiteboards.
The Follow-Up Trap
The Fan Trap: Joins That Inflate Metrics
The Tell: Words That Signal This Pattern
- ▸You're joining two tables that both have multiple rows per join key
- ▸Aggregated numbers are higher than expected but not obviously wrong
- ▸A SUM works fine for one table alone but inflates when you join
- ▸The interviewer says "the revenue numbers look too high"
- ▸You're joining a fact to a fact through a shared dimension
The Scenario the Interviewer Will Draw on the Whiteboard
| customer_id | order_id | order_amount |
|---|---|---|
| alice | O1 | $100 |
| alice | O2 | $200 |
| customer_id | shipment_id | ship_cost |
|---|---|---|
| alice | S1 | $10 |
| alice | S2 | $15 |
| alice | S3 | $12 |
| customer_id | order_id | order_amount | shipment_id | ship_cost |
|---|---|---|---|---|
| alice | O1 | $100 | S1 | $10 |
| alice | O1 | $100 | S2 | $15 |
| alice | O1 | $100 | S3 | $12 |
| alice | O2 | $200 | S1 | $10 |
| alice | O2 | $200 | S2 | $15 |
| alice | O2 | $200 | S3 | $12 |
The Answer: Pre-Aggregate Before Joining
The fan trap: joining one customer to TWO facts at different grains (orders and calls) multiplies the rows, inflating SUM(amount). Fix: aggregate each fact to the join grain BEFORE joining.
The principle: never join two fact tables at different grains. Pre-aggregate each to the shared dimension grain first, then join the aggregates. This is the single most important query pattern for avoiding fan traps.
- Pre-aggregate each fact to the join grain before combining
- Use CTEs to make the grain of each subquery explicit
- Verify row counts before and after the join
- Join two fact tables directly on a shared dimension key
- Assume SUM will be correct without checking grain compatibility
- Ignore row count differences between expected and actual
What the Interviewer Writes on the Scorecard
- Joins the two fact tables directly without noticing the grain mismatch
- "We'd just join orders and shipments on customer_id"
- Cannot explain why the numbers are inflated when asked
- Identifies the fan trap before writing any SQL
- "These are different grains, so I'll aggregate each to customer level first"
- Mentions that this is a 2xN cartesian product problem
The Chasm Trap: Missing Relationships
The Tell: Words That Signal This Pattern
- ▸Row counts are lower than expected after a join
- ▸"Some customers are missing from the report"
- ▸You're joining Fact A to Dimension to Fact B, and the dimension is sparse
- ▸The interviewer asks about customers with orders but no shipments (or vice versa)
- ▸An INNER JOIN path drops entities that exist in one fact but not the other
The Scenario the Interviewer Will Draw on the Whiteboard
| customer_id | order_amount |
|---|---|
| alice | $100 |
| bob | $200 |
| carol | $150 |
| customer_id | ship_cost |
|---|---|
| alice | $10 |
| dave | $25 |
| customer_id | order_amount | ship_cost | status |
|---|---|---|---|
| alice | $100 | $10 | Returned by INNER JOIN |
| bob | $200 | NULL | DROPPED |
| carol | $150 | NULL | DROPPED |
| dave | NULL | $25 | DROPPED |
The Answer: FULL OUTER JOIN or Separate Queries
Two approaches, and knowing which one to reach for and why is the interview signal:
- Aggregate each fact to the dimension grain, then FULL OUTER JOIN the results. This preserves all entities from both sides. Best when you need a single combined view.
- Run independent queries against each fact table. Combine in the presentation layer. Best when the two facts have fundamentally different semantics and joining them is misleading.
- Use COALESCE(orders.customer_id, shipments.customer_id) after a FULL OUTER JOIN to produce a clean key column with no NULLs.
Fan Trap vs Chasm Trap
- Too MANY rows after join
- Aggregates are inflated
- Caused by many-to-many through a dimension
- Fix: pre-aggregate before joining
- Symptom: numbers are too HIGH
- Too FEW rows after join
- Entities are silently dropped
- Caused by INNER JOIN through sparse dimension
- Fix: FULL OUTER JOIN or separate queries
- Symptom: numbers are too LOW
The strongest interview move: when you see a join between two fact tables, say 'I need to check for both fan traps and chasm traps here.' Naming both unprompted signals deep modeling experience.
Grain as a Communication Tool
The Bridge Move
Grain is your lever for expanding any modeling question into a system design conversation. Every time you state a grain, you create a natural bridge to discuss: partitioning strategy, storage cost, query performance, and downstream consumers. This is how you get leveled up in the interview loop.
Red Flag Phrases to Avoid
Vocabulary That Signals Seniority
| Junior Phrasing | Senior Phrasing |
|---|---|
| "Each row is an order" | "The grain is one row per order line item, unique on (order_id, line_seq)" |
| "We'd join these tables" | "These are at different grains, so I'd pre-aggregate before joining" |
| "The numbers are wrong" | "This looks like a fan trap from joining at mismatched grains" |
| "Some rows are missing" | "The INNER JOIN is creating a chasm trap; entities without matches are dropped" |
| "I'd add all the columns" | "Which attributes are at this grain vs a different grain?" |
The Closing Move
- ▸"The grain is one row per X, enforced by a unique constraint on (A, B)."
- ▸"We pre-aggregate to the join grain before combining facts to avoid fan traps."
- ▸"We use FULL OUTER JOIN to prevent chasm traps from silently dropping entities."
> You are in a Meta data engineering interview. The interviewer asks you to design a fact table for ad impressions and clicks.
Wrong grain = wrong numbers; fan traps multiply your metrics silently
- Category
- Data Modeling
- Difficulty
- advanced
- Duration
- 25 minutes
- Challenges
- 0 hands-on challenges
Topics covered: "What Is the Grain of This Table?", Defining Grain: One Row Represents What?, The Fan Trap: Joins That Inflate Metrics, The Chasm Trap: Missing Relationships, Grain as a Communication Tool
Lesson Sections
- "What Is the Grain of This Table?" (concepts: dmGrainDefinition)
Grain is not a modeling step. It is THE modeling step. Everything else follows from it: which columns belong in the table, which joins are valid, which aggregations produce correct numbers. Getting grain wrong does not produce an error message. It produces wrong numbers that look right. That is why interviewers test it. What They're Really Testing The 60-Second Framework Step 3 is the strong-hire signal. Asking the interviewer a clarifying question about grain shows you have been burned by wrong
- Defining Grain: One Row Represents What? (concepts: dmGrainDefinition)
Your grain statement answer: 'The grain is a contract. Every row represents exactly one instance of X. If I can write a GROUP BY on the grain columns and get COUNT(*) > 1 for any group, the grain is violated.' Say this, then immediately write the validation query. The interviewer is checking whether you can enforce grain, not just define it. How Interviewers Grade Your Grain Statement Watch the progression from vague to interview-grade: The jump from 'good' to 'strong hire' is acknowledging that
- The Fan Trap: Joins That Inflate Metrics (concepts: dmGrainDefinition)
Your fan trap answer: 'A fan trap happens when I join two facts through a shared dimension and one side has multiple rows per key. The join fans out, duplicating the other side. My SUM now counts revenue 3x because each order was duplicated once per shipment. No error message, no warning, just wrong numbers that look plausible.' Say 'no error message' explicitly. That is what makes fan traps dangerous and why interviewers test them. The Tell: Words That Signal This Pattern The Scenario the Inter
- The Chasm Trap: Missing Relationships (concepts: dmManyToMany)
Your chasm trap answer: 'A chasm trap is the inverse. Instead of too many rows, I get too few. An INNER JOIN through a sparse dimension silently drops entities that exist on only one side. Bob has orders but no shipments. The INNER JOIN drops Bob entirely. My total orders metric just lost 70% of its value.' Pair this with the fan trap to show you check for both. The Tell: Words That Signal This Pattern The Scenario the Interviewer Will Draw on the Whiteboard Set up the scenario for the interview
- Grain as a Communication Tool (concepts: dmGrainDefinition)
Everything in this lesson so far has been about grain as a technical concept. This section is about grain as a communication strategy. In an interview, the way you talk about grain determines your level. Junior candidates treat grain as a checkbox. Senior candidates use it as the anchor for every subsequent design decision. The Bridge Move Red Flag Phrases to Avoid Vocabulary That Signals Seniority The Closing Move At the end of any modeling question, circle back to grain. 'So to summarize: the