DATA ARCHITECTUREGrain

Grain: What One Row Means

How data engineers design data that lasts — pick a topic on the left and its full breakdown loads here: the mental model, ERDs and worked schemas, trade-offs, edge cases, and the decisions that separate a durable model from a fragile one.

18 min readTopics chapter readerLevel · Foundations
01 · Orientation

What You'll Master Here

Grain is the answer to "what does one row mean?" State it as "one row per ___" before you build anything.

4 min · Topic 1 of 10

If you remember one idea from this entire course, make it this one: the grain of a table is what a single row represents. It sounds almost too simple, and that is exactly why it is the most skipped and most expensive decision in data modeling.

Step through the three moves below. One solar dataset, three ordinary questions, three wrong answers — and not one of them raised an error. Breaking that silence is what this chapter is for.

Core mental model

Grain is the answer to "what does one row mean?" State it as "one row per ___" before you build anything.

Why it matters

Grain decides what every count and sum means. Get it wrong and there is no error message, just inflated, untrustworthy numbers that someone eventually makes a decision on.

grain
What a single row of a table represents (one order, one item, one day).
atomic grain
The lowest, most detailed level of a fact (one row per individual event).
fan-out
Row multiplication when joining a coarse-grain table to a finer-grain one.
mixed grain
A single table whose rows represent different things; a design error.
One dataset, three grain failuresA solar farm logs each inverter every five minutes.
Move 1 · State what one row is

A solar operator asks: “how many inverters are reporting?”

inverter_idread_atkwh
A-109:0012
A-109:0511
A-209:009
A-209:0510
B-109:006
B-109:055
What you getcount(*) = 6What is truecount(distinct inverter_id) = 3
One row of inverter_readings is one inverter at one five-minute interval — not one inverter, and not one site. Until that sentence exists, every count is a number with no unit.
What you’ll be able to do1/3
  • State what one row is
  • One meaning per table
  • Join without inflating
Every one of these three queries ran without an error. That is what makes grain bugs expensive.
Common mistake

Building a table without saying its grain out loud first. You discover the grain only when a metric looks wrong, after code and dashboards already depend on it.

Better habit

Write "one row per ___" before creating any table.

Confirm what count(*) means before trusting it.

Suspect grain first whenever a number looks too high.

The big idea

Almost every "the numbers are wrong" incident is a grain problem in disguise: counting or summing at a grain different from what the question assumed.

Remember this

Grain is what one row means; naming it explicitly before building is the single highest-leverage habit in data modeling.