Skip to content
QSWEQB
mediumDesignConceptMidSeniorStaff

In a modern columnar warehouse, would you still build a star schema or just use one wide table?

Settle the grain first - what one fact row means. A star still earns its place for conformed dimensions and type-2 history, while a wide table is a serving artefact for one known pattern. A warehouse differs from a lake by having a modelled contract, not by where the bytes live.

6 min readUpdated 2026-07-26

What the interviewer is scoring

  • Does the candidate state the grain as a sentence before discussing table shape at all
  • Whether they argue for the star on semantics and history rather than on join performance
  • That they can distinguish a surrogate key from a business key and say why the fact stores the former
  • Whether the warehouse-versus-lake answer is about contracts and ownership, not about file formats
  • Does the candidate treat the wide table as a derived layer rather than as a replacement for the model

Answer

Grain is the first decision, and it is not negotiable afterwards

Before arguing about shape, finish this sentence: one row in this fact table is one ____. One completed order. One order line. One daily snapshot of a subscription. One shipment scan event. Everything downstream is determined by that choice - which dimensions can attach, which measures are additive, and which questions the table simply cannot answer.

Getting it wrong is expensive in a specific way. If you declare order grain and later need line-level discounting, you cannot decompose the rows you already wrote; you rebuild. If you declare a grain and then quietly mix two - some rows are orders, some are order lines - every sum over the table is wrong and nothing tells you. And joining two facts of different grain directly fans out the coarser one: an order joined to its three lines produces three copies of the order total, so the revenue figure triples. The correct pattern is to aggregate one fact to the other's grain first, or to join both to shared dimensions and compare the aggregates rather than the rows.

State the grain out loud in an interview even when nobody asked. It is the single clearest signal that you have built these tables rather than read about them.

What the star buys you once joins are cheap

The historical case for dimensional modelling was partly about join cost on row-oriented databases, and that part has genuinely weakened. A columnar warehouse only reads the columns a query names, prunes files by partition and by column statistics, and executes joins vectorised across a scaled-out cluster. A hundred-column table costs you nothing on a query that touches four of them. If your case for the star is "joins are faster", a good interviewer will dismantle it.

The case that survives is about semantics and change. A conformed dimension is one place where "customer segment" is defined, so a dashboard about churn and a dashboard about revenue cannot silently disagree on who counts as enterprise. It is also one place to fix an error: correct dim_product and every fact referencing it reports correctly, whereas correcting a denormalised attribute means rewriting the history of every wide table that copied it. And it is the only structure that lets you answer "what was true at the time" without keeping a second copy of the past, which is the subject of the next section.

When a wide table is the right artefact

Frame it as a layer, not as a rival. A one-big-table model - denormalised, all attributes resolved, one row per event with everything a report needs - is an excellent serving shape for one well-understood access pattern. A BI extract behind a dashboard, or a feature table for a model, both benefit: no joins for the consumer, predictable cost, and a tool-friendly flat schema.

What you pay for it is reusability and change tolerance. The wide table encodes the join decisions of whoever built it, so the next question that needs a slightly different slice gets a second wide table rather than a reuse. Attribute changes require a rebuild rather than an update in one dimension. And because the attributes are copied, a wide table built today and a wide table built next quarter can disagree, which is exactly the failure the conformed dimension existed to prevent. So: model in the star, materialise wide tables from it where a consumer's pattern justifies one, and treat those wide tables as disposable.

Build the star because you do not yet know every question; build the wide table because you do know this one. They are different layers with different lifetimes, and answering "either/or" misses that.

Slowly changing dimensions, and the join that breaks them

A dimension attribute changes and you have to decide what history means. Type 1 overwrites in place, so all reporting immediately shows current values and the past is gone - correct for fixing a typo, wrong for anything you will be asked to explain. Type 2 keeps the old row and inserts a new one with its own surrogate key and a validity interval, so history is preserved and the past stays reproducible. Type 3 adds a column such as previous_segment, which handles exactly one step of history and is rarely what you want. Type 0 means the attribute is deliberately immutable, like an original signup channel.

A type-2 dimension looks like this:

dim_customer_keycustomer_idsegmentvalid_fromvalid_tois_current
8801C-417SMB2024-01-012026-03-14false
9422C-417Enterprise2026-03-149999-12-31true

The surrogate key exists precisely because the business key C-417 is no longer unique in this table. The fact row for an order placed in 2025 stores 8801, resolved once at load time, and therefore continues to report against the segment that was in force when the order happened - even after the customer is upgraded, and even after a future reorganisation of segments.

-- Resolve the dimension version in force at the event, once, on load.
SELECT o.order_id,
       d.dim_customer_key,
       o.amount_minor
FROM   stg_orders o
JOIN   dim_customer d
  ON   d.customer_id = o.customer_id
 AND   o.ordered_at >= d.valid_from
 AND   o.ordered_at <  d.valid_to;   -- half-open: no event can match two versions

The half-open interval is not a detail. If valid_to of one row equals valid_from of the next and you use BETWEEN, an event landing exactly on the boundary matches both versions and that fact row is duplicated.

Now the mistake that quietly ruins type-2 reporting: joining the fact to the dimension on the business key rather than the surrogate key. The join looks reasonable, it compiles, and it produces two rows per fact for every customer that has ever changed - so measures inflate by the number of historical versions. If someone patches that by adding AND is_current = true, the duplication disappears and is replaced by something harder to spot: every historical order is now reported under the customer's present segment. The dashboard becomes internally consistent and no longer describes what happened. That is why the surrogate-key join is presented as a rule rather than a preference.

Warehouse, lake, and what the lakehouse changed

The distinction is not storage medium and it is not file format. A data warehouse is a modelled, curated, schema-on-write environment: tables have declared structure, transformations are versioned, someone owns correctness, and a consumer can query it without knowing how the data arrived. A data lake is raw and semi-processed files in object storage with schema-on-read: anything can land, cheaply, in any format, and the burden of interpreting it - working out which columns are trustworthy, which files are duplicates, which partition is half-written - sits with each consumer, separately, forever. That last part is the real cost, and it is organisational rather than technical.

Table formats such as Iceberg and Delta Lake closed much of the technical gap by putting a metadata layer over lake files: atomic commits, snapshot isolation so a reader never sees a half-finished write, schema evolution, and time travel to a previous snapshot. That is what makes a "lakehouse" a coherent idea rather than a slogan, and it is why a warehouse engine can now query lake storage directly.

What no table format supplies is the modelling. Declared grain, conformed dimensions, tracked history and an owned metric definition are decisions people make, and skipping them produces the same swamp on Iceberg that it produced on bare Parquet.

Likely follow-ups

  • A dimension attribute was recorded wrongly for the last six months. What do you change, and does it depend on the SCD type?
  • How do you report on a customer count when your only fact is at order-line grain?
  • Where would you put a metric definition so two dashboards cannot disagree about it?
  • What does a table format like Iceberg or Delta give you that a folder of Parquet does not?

Related questions

Further reading

dimensional-modellingstar-schemascdgrainlakehouse