Skip to content

Choose a dataset mode

A decision flow for picking cube, lattice, hybrid, or rows per dataset, plus how to tell when the server picked one you did not expect.

Each dataset in a dashboard picks one of four materializations independently. The choice is mechanical once you know two things about the dataset: whether any measure is non-additive, and how its filters work.

This page is the decision flow. For what each mode actually is and why the positioning runs the way it does, see Datasets and the four modes.

The flow

Answer in order. The first rule that fires is your answer.

1. Is every measure additive?

Additive means summing partial totals gives the right answer. sum, count, min, and max are additive.

Non-additive means it is not. Any of these makes the dataset non-additive:

  • count(distinct ...)
  • avg
  • median
  • percentile_cont or any percentile
  • stddev
  • variance
  • mode()

If every measure is additive, use cube. Stop here.

2. Is every dimension low-cardinality?

That means every field the dashboard filters, charts, or tabulates on has a small bounded set of values, and dates are bucketed rather than raw timestamps.

If not, use rows. Skip to step 4.

3. Are all the filters single-select?

A single-select filter fixes one value at a time.

If yes, use lattice. Stop here.

If a multi-select or range filter has to stay exact on a non-composable measure, use hybrid. Non-composable means a distinct count, a true average, a median, a percentile, a standard deviation, a variance, or a mode. A sum, count, min, max, or a ratio of them composes across selected values without help, so those do not force hybrid.

4. If you landed on rows, decide where the bytes go

A rows dataset on a warehouse connection can declare Parquet offload, which moves its rows out of the shared inline budget so it can grow between publishes.

Three conditions, and each is a hard publish error rather than a surprise:

  • Only a rows dataset. Never cube, lattice, or hybrid.
  • Only on a warehouse connection, never on self.
  • At most two per dashboard.

SQL Server has no Parquet path, and no rows or hybrid mode at all. See Connect SQL Server.

The short form

IfUse
Every measure is additivecube
Something is non-additive, all dimensions low-cardinality, all filters single-selectlattice
As above, but a multi-select or range filter must stay exact on a non-composable measurehybrid
A dimension cannot be bounded, or you need row-level detailrows

Cost and power both run cube then lattice then hybrid then rows. Take the first one that is correct, not the most capable one. The reasoning behind that ordering is in how the choice is actually made.

You do not have to declare it

Leave mode off and the server auto-selects cube or lattice from the measures and dimensions. rows and hybrid ship row-level bytes, so they must be declared explicitly.

The publish report's mode_choices says what each dataset resolved to and why, for example:

main -> lattice: a distinct count over low-cardinality dimensions

Read it. A mode you did not expect is usually the server noticing a measure is not what you thought it was, which is worth knowing before it becomes a wrong number.

The rule is enforced, so a wrong choice is loud

A cube dataset whose SQL computes a non-additive aggregate is rejected at publish, naming the construct. Why it refuses rather than warns is explained here. A cube measure declared with a non-additive aggregation is a schema error. Validation warns about the same SQL earlier, when your AI passes the dataset's mode.

The enforcement reads the SQL text, so it has a blind spot

A ratio or a distinct count computed inside a CTE or a subquery, then selected as a plain column, reads as additive from the outside and passes every check.

That is why Verify your numbers is a required step and not a nicety. The check there is exactly what catches a mode chosen wrongly for this reason.

A lattice must stay bounded

A lattice precomputes one cell per filter state, so its size is roughly the product of each dimension's number of values plus one. Every dimension declares its bound: a list of values for a category, or buckets for a date.

If it grows past the inline cap, the fix is to drop or bound a dimension. A lattice can never offload to Parquet, so there is no way to buy your way out of an unbounded one. The arithmetic is in the lattice cell budget, and offloading to Parquet covers what Parquet does and does not buy.

Check it worked

  1. mode_choices in the publish report names the mode you intended for each dataset.
  2. The publish was not refused for a non-additive aggregate in a cube.
  3. You ran Verify your numbers on each additive measure, and the two legs agreed. That is the step that confirms the mode was right, rather than merely accepted.