skip to content

Dimensional Modeling

Kimball-style dimensional modeling: splitting the world into measurable business events and the descriptive context you slice them by. Interviewers hand you a business process and expect a star schema with a defensible grain within minutes.

on this pageshow

explore

questions

page 1 of 2

What is an aggregate fact table in a dimensional model, and why keep the atomic fact table too?

level: juniorimportance: must knowfreq 60%

answer

  1. one row per what, and how many fewer?
  2. same measures, different grain
  3. who rebuilds whom after a restatement?
  4. the detail layer is the source of truth
  5. derived summary, declared grain, atomic reference

basics

~20 s

An aggregate fact table stores the same measures as an atomic fact table, pre-summed to a coarser grain such as month-by-product. It is a derived performance copy; the atomic table stays as the source of truth for detail and rebuilds.

solid answer

~50 s

An **aggregate fact table** (or summary fact) carries the same measures as the atomic fact table but at a deliberately coarser grain — one row per product per store per month instead of one row per order line, say. A dashboard asking for monthly revenue by category then reads thousands of rows instead of billions, so it returns far faster. The key point is that an aggregate is **derived, never authoritative**. You keep the atomic fact table because it answers what the aggregate cannot: drilling to an individual order, filtering on an attribute the aggregate dropped, slicing at day grain. It is also what you rebuild the aggregate from when a load is restated, and the reference you reconcile against when numbers disagree. Aggregates are an optimization layered on a complete atomic model, never a substitute for one.

code

sql · 20 lines
sql
-- atomic fact: one row per order line
CREATE TABLE fact_sales (
  date_key         INTEGER      NOT NULL,
  product_key      INTEGER      NOT NULL,
  store_key        INTEGER      NOT NULL,
  order_number     VARCHAR(20)  NOT NULL,  -- degenerate dimension
  quantity         INTEGER      NOT NULL,
  revenue_amount   DECIMAL(18,2) NOT NULL
);

-- aggregate: one row per product per store per month
CREATE TABLE agg_sales_month_product_store (
  month_key        INTEGER      NOT NULL,
  product_key      INTEGER      NOT NULL,
  store_key        INTEGER      NOT NULL,
  quantity         INTEGER      NOT NULL,
  revenue_amount   DECIMAL(18,2) NOT NULL,
  order_line_count INTEGER      NOT NULL,
  PRIMARY KEY (month_key, product_key, store_key)
);

go deeper

for a junior

Be ready to say in one sentence what an aggregate fact table is: the same measures, pre-summed to a coarser grain, with the detailed table kept alongside it. Name a concrete example such as daily order lines rolled up to product-by-month.

for a middle

Explain the mechanics: declare the aggregate's grain, enforce it as a unique key, and build it by summing the atomic star rather than re-reading the source so the two cannot diverge.

for a senior

Show judgment about which aggregates earn their keep — the row-count reduction, the family of queries served, and the permanent cost in load logic, tests and reconciliation that every summary table adds.

for a principal

Own the policy: how many summary tables the platform supports, who may publish one, and how consumers are routed to them so the organization never ends up with several teams' rollups quietly reporting different revenue.

## What an aggregate fact table is A **fact table** in a dimensional model holds the numeric measurements of a business process — revenue, quantity, duration — plus foreign keys to the dimension tables that describe them (date, product, store, customer). The **grain** is the sentence stating what exactly one row means: "one row per order line" or "one row per product per store per month". An **aggregate fact table** is a second fact table over the same business process, built at a *coarser* grain by summing the atomic rows. If the atomic table is one row per order line per day, an aggregate might be one row per product per store per month, with `quantity` and `revenue_amount` summed inside each of those buckets. Nothing new is measured; the same numbers are simply pre-added. The payoff is row count. A retailer with a billion order lines a year may have only a few million product-store-month combinations. Any report whose result is already at or above the aggregate's grain can be answered by scanning the small table instead of the large one. ## The grain rule applies to aggregates too Design an aggregate exactly the way you design any fact table: declare the grain first, in one sentence, and make it a real uniqueness constraint. `(month_key, product_key, store_key)` should be unique. If two rows can share that key, the aggregate double-counts the moment anyone sums it, and the failure is silent — the report still renders, it is just wrong. A good habit is to write a standing test that the declared grain holds: ```sql SELECT month_key, product_key, store_key, COUNT(*) FROM agg_sales_month_product_store GROUP BY month_key, product_key, store_key HAVING COUNT(*) > 1; ``` ## Why the atomic fact table never goes away Three reasons, and interviewers want all three. **It answers questions the aggregate cannot.** The aggregate has thrown away dimensions and attributes: day, order number, customer, promotion. Any question below its grain, or filtered by a dropped attribute, has no answer in the summary. Aggregates serve a known family of queries; the atomic layer serves the unknown ones, which in analytics is most of them. **It is the rebuild source.** When a source system restates last quarter, or a bug in a transformation is fixed, you correct the atomic layer and regenerate the affected aggregate rows from it. If the aggregate were the only copy, an incorrect summarization would be unrecoverable — the detail needed to recompute it would not exist. **It is the reconciliation reference.** The only way to know an aggregate is still telling the truth is to compare it against the atomic table by period. Without a detailed layer there is nothing to compare against, and drift becomes undetectable. ## How the aggregate is built Always by summing the atomic table, never by re-reading the source system. Deriving from the source invites the two paths to diverge — a different filter, a different currency conversion, a different treatment of cancelled orders — and then the aggregate and the star disagree for reasons nobody can trace. ```sql INSERT INTO agg_sales_month_product_store SELECT d.month_key, f.product_key, f.store_key, SUM(f.quantity), SUM(f.revenue_amount), COUNT(*) FROM fact_sales f JOIN dim_date d ON d.date_key = f.date_key GROUP BY d.month_key, f.product_key, f.store_key; ``` ## Choosing which aggregates to build Build from observed query patterns, not guesses. The candidates are the grains your dashboards actually land on repeatedly — usually a date rollup (day to month) combined with a hierarchy rollup (product to category, store to region). An aggregate is worth its maintenance cost when it collapses a large row count into a small one and serves a whole family of reports rather than a single chart. A summary that only removes a modest fraction of the rows buys little and still has to be loaded, tested and reconciled forever. ## Aggregates should be invisible to the user The classic dimensional-design rule is that users and their SQL should not have to know which table they are hitting. If analysts must remember to write `agg_sales_month_product_store` for monthly questions and `fact_sales` for daily ones, half of them will use the wrong one and totals will disagree across the company. Routing belongs in the BI or semantic layer that sits above both tables, and it is only safe when the aggregate joins to conformed, shrunken versions of the same dimensions. ## What interviewers listen for That you say *derived*, that you declare a grain, that you keep the atomic layer, and that you build the summary from the star rather than from the source. Candidates who describe an aggregate as "a table we made because the dashboard was slow" and cannot say what one row means have not designed one.

  • Should the aggregate be built from the atomic fact table or straight from the source system?
    From the atomic fact table. Deriving it from the source lets the two paths diverge — a different filter for cancelled orders, a different currency rule — and then the summary and the star disagree for reasons nobody can trace. Summing the star guarantees the aggregate is a true rollup of the numbers the detail layer already publishes.
  • How do you decide an aggregate is not worth building?
    When it barely reduces the row count, when it serves one chart rather than a family of reports, or when the dimensions it would need at that grain do not exist yet. Every aggregate is permanent load logic, a permanent test surface and a permanent reconciliation obligation, so the saving must be large and recurring.
  • What uniqueness guarantee should an aggregate fact table carry?
    Its declared grain must be a unique key — for a month-product-store aggregate, (month_key, product_key, store_key). Enforce it with a constraint or a standing test that groups by those columns and fails on any count above one. A duplicated grain row double-counts silently: the report still renders, it is simply wrong.

An aggregate is a printed monthly bank statement summary; the atomic fact table is the full transaction ledger. The summary is faster to read and always reproducible from the ledger — never the other way round.

saying these in an interview costs you the question

  • Calling the aggregate the source of truth and dropping the detail
  • Building the summary from the source system instead of the star
  • Never declaring what one aggregate row means
  • Assuming any question can be answered from the summary
  • Treating an aggregate as a filtered subset rather than a coarser grain

context

open as a page

What makes a dimension conformed across two star schemas in a warehouse?

level: juniorimportance: must knowfreq 72%

basics

~20 s

A conformed dimension is one shared dimension - the same table, or copies with identical keys, attribute names and attribute values - used by several fact tables, so grouping or filtering by it means exactly the same thing in every star.

open as a page

What is a degenerate dimension, and why does an order number stay in the fact row?

level: juniorimportance: must knowfreq 60%

basics

~20 s

A degenerate dimension is an operational identifier such as an order or invoice number stored directly in the fact table with no dimension table behind it, because every descriptive attribute it would carry already lives in other dimensions.

open as a page

What is a role-playing dimension, and how do you join one date dimension three times?

level: juniorimportance: must knowfreq 58%

basics

~20 s

A role-playing dimension is one physical dimension joined to the same fact table several times under different meanings — order date, ship date and due date — usually exposed as one view or alias per role so column names stay unambiguous.

open as a page

In a dimensional model, what distinguishes a fact table from a dimension table?

level: juniorimportance: must knowfreq 85%

basics

~20 s

A fact table holds the numeric measurements of a business process, one row per event at a declared grain, plus foreign keys to dimensions. A dimension table holds the descriptive attributes you filter, group and label by.

open as a page

What is a transaction fact table, and how does it differ from a periodic snapshot fact table?

level: juniorimportance: must knowfreq 72%

basics

~20 s

A transaction fact table holds one row per business event at the moment it happens, insert-only and sparse. A periodic snapshot holds one row per entity per fixed interval, such as a daily account balance, recorded even when nothing happened.

open as a page

What is the difference between a star schema and a snowflake schema?

level: juniorimportance: must knowfreq 85%

basics

~20 s

A star schema keeps each dimension as one wide, denormalized table joined directly to the fact table. A snowflake splits those dimensions into normalized hierarchy sub-tables, so reaching an attribute costs extra joins. The fact table is identical in both.

open as a page

Why must a monthly aggregate fact table join to a shrunken date dimension derived from the day-level one?

level: middleimportance: must knowfreq 50%

basics

~20 s

Because the month dimension must be a rollup of the day dimension's attributes and labels. Building it separately lets month names, fiscal periods and hierarchies drift, so the aggregate and the atomic star report different things for the same month.

open as a page

What does it mean to declare the grain of a fact table before choosing its dimensions?

level: middleimportance: must knowfreq 82%

basics

~20 s

Declaring the grain states in one business sentence what a single fact row represents, such as one row per order line. It is fixed before dimensions and measures, because both are only valid at exactly that level.

open as a page

Why is an account balance called a semi-additive fact, and how should a report roll it up?

level: middleimportance: must knowfreq 70%

basics

~20 s

A balance is a level, not a flow: it can be summed across accounts, products or regions but not across time, because each period restates the same money. Roll it up over time with the period-end value or an average, never a sum.

open as a page

What is a bridge table in a dimensional model, and what problem does it solve?

level: middleimportance: must knowfreq 62%

basics

~20 s

A bridge table resolves a many-to-many between a fact and a dimension: one row per group member, holding a group key, a dimension key and often a weighting factor. The fact keeps its original grain and still reaches every member.

open as a page

Why is a star schema the default choice over a snowflake for analyst-facing marts?

level: middleimportance: must knowfreq 70%

basics

~20 s

Dimensions are tiny next to fact tables, so normalizing them saves almost no storage while adding joins to every query and forcing analysts to know the hierarchy. A star trades cheap duplication for one obvious join path.

open as a page

How do you combine measures from two fact tables in one report without inflating totals?

level: seniorimportance: must knowfreq 62%

basics

~20 s

Query each fact table separately, aggregating each to the same conformed dimension attributes, then join the two result sets on those attributes. This is a drill-across. Joining the fact tables directly to each other multiplies rows and inflates every measure.

open as a page

Why does a bridge-table join double-count fact measures, and how does a weighting factor fix it?

level: seniorimportance: must knowfreq 48%

basics

~20 s

The join fans out: a fact row matches one bridge row per group member, so the engine returns a copy of the measure for each. Multiplying by a weighting factor whose values sum to 1.0 within a group produces allocated amounts that re-total correctly.

open as a page

How do you model a fixed-depth product hierarchy like department, category and brand in a warehouse dimension?

level: juniorimportance: should knowfreq 52%

basics

~20 s

Put one column per level on every dimension row, so drilling down is just changing the GROUP BY column and no extra joins appear. This works only when the hierarchy has fixed depth and each member has exactly one parent.

open as a page

Which measure columns belong in an aggregate fact table so further rollups stay correct?

level: middleimportance: should knowfreq 45%

basics

~20 s

Store additive components — sums and counts — never stored averages or percentages. Ratios are recomputed at query time from the components; distinct counts do not roll up at all and need the atomic table or a sketch column.

open as a page

What is an enterprise bus matrix, and how does a team use it to plan a warehouse?

level: middleimportance: should knowfreq 52%

basics

~20 s

The enterprise bus matrix is a grid whose rows are business processes - each a candidate fact table - and whose columns are dimensions, ticked where that process uses that dimension. It shows which dimensions must be conformed and what to build in which order.

open as a page

What are conformed facts, and what breaks when two marts define revenue differently?

level: middleimportance: should knowfreq 45%

basics

~20 s

Conformed facts are measures that share a name only when they share an identical definition - same calculation, units, currency and inclusion rules. When two marts both call something revenue but compute it differently, cross-mart comparison produces confidently wrong numbers with no error.

open as a page

What is a junk dimension, and when do you collapse fact-table flags into one?

level: middleimportance: should knowfreq 55%

basics

~20 s

A junk dimension collapses several low-cardinality flags and codes — order type, payment type, gift flag, ship mode — into one small dimension whose rows are the distinct combinations, replacing many narrow columns in the fact row with a single key.

open as a page

What are the four steps of Kimball's dimensional design process, in order?

level: middleimportance: should knowfreq 62%

basics

~20 s

Choose the business process, declare the grain of a single fact row, identify the dimensions that describe that grain, then identify the facts measured at it. Each step is only valid at the grain fixed in step two.

open as a page

What is an accumulating snapshot fact table, and why are its rows updated in place?

level: middleimportance: should knowfreq 52%

basics

~20 s

An accumulating snapshot holds one row per pipeline instance — an order, a claim, an application — with a date key per milestone and lag measures between them. The row is updated as the instance advances, because the whole point is one current row per pipeline.

open as a page

When is snowflaking a dimension into normalized sub-tables actually the right call?

level: middleimportance: should knowfreq 50%

basics

~20 s

Snowflake when a hierarchy level is huge and repeated, is mastered and governed upstream as its own reference entity, or changes on a different cadence than the dimension. Even then, most teams normalize the load layer and publish a flattened dimension.

open as a page

Your monthly revenue rollup no longer matches the atomic fact table for closed months — what causes do you check?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Check late-arriving or restated fact rows in months the rebuild window no longer covers, a dimension attribute change that re-parented history, and definition drift where filters or exclusions differ between the two. Then add a reconciliation test by period.

open as a page

What must an aggregate fact table guarantee before a BI layer can silently substitute it?

level: seniorimportance: should knowfreq 35%

basics

~20 s

The aggregate must expose identical measure definitions, join to shrunken conformed dimensions whose attribute values match the atomic star, and stay in sync with it. Queries needing a dropped attribute or a finer grain must fall back to the atomic table.

open as a page

A junk dimension grew from 200 rows to 3 million after a column was added — what went wrong?

level: seniorimportance: should knowfreq 28%

basics

~20 s

Almost certainly a high-cardinality or unnormalized value was admitted — an identifier, a timestamp, an amount, or codes differing only by case or whitespace — so the combination count multiplied. Diagnose per-column distinct counts, then move the offender out.

open as a page

When does a mini-dimension beat keeping fast-changing attributes on a customer dimension?

level: seniorimportance: should knowfreq 34%

basics

~20 s

Split attributes into a mini-dimension when a large dimension carries a few attributes that change often — income band, credit score band, segment — so versioning the whole row would multiply millions of wide rows. The fact then carries both keys.

open as a page

An order fact table repeats the order-level shipping charge on every line-item row — what breaks?

level: seniorimportance: should knowfreq 55%

basics

~20 s

Summing the shipping charge multiplies it by the number of lines, because it is measured at order grain, not line grain. Fix it by allocating the charge across lines or moving it to an order-grain fact table.

open as a page

A stored margin_pct column in a fact table sums to 812% on a dashboard — how do you fix the model?

level: seniorimportance: should knowfreq 56%

basics

~20 s

Percentages and ratios are non-additive: they cannot be summed across any dimension. Store the fully additive numerator and denominator in the fact row instead, and compute the ratio at query time as sum of numerator over sum of denominator.

open as a page

How do you roll up facts through a ragged, variable-depth org hierarchy in a star schema?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Build a hierarchy bridge with one row per ancestor-descendant pair, including each node paired with itself, carrying the depth between them and top/bottom flags. Join facts on the descendant key and filter on the ancestor to total any subtree at any depth.

open as a page

A snowflaked product dimension spans seven tables and every dashboard joins all of them — how would you restructure it?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Flatten it into one wide dimension at the product grain, keeping the normalized tables as the load and reference layer. Verify each level contributes at most one row per product, then publish the flat table and reconcile totals against the old joins before cutting dashboards over.

open as a page

showing 1–30 of 41