skip to content

In Tableau, how do FIXED, INCLUDE and EXCLUDE LOD expressions differ in granularity?

level: middleimportance: must knowfreq 82%

answer

  1. one of the three ignores the shelves
  2. two of them move with the view
  3. plus the view, minus the view, or neither
  4. braces declare a grain before the colon

basics

~20 s

FIXED computes at exactly the dimensions listed and ignores the view. INCLUDE computes at the view's dimensions plus the ones listed, EXCLUDE at the view's dimensions minus them, so both move when the view changes.

solid answer

~50 s

All three are level of detail expressions written in braces as `{ KEYWORD [dimensions] : AGG(...) }`, and they let one calculation evaluate at a grain other than the view's. **FIXED** is absolute: `{FIXED [Customer ID] : SUM([Sales])}` is each customer's lifetime sales no matter what is on the shelves, and `{FIXED : SUM([Sales])}` with no dimensions is a single whole-table number. **INCLUDE** is the view's grain plus what you list, so `AVG({INCLUDE [Customer ID] : SUM([Sales])})` gives average sales per customer at whatever grain the view happens to be. **EXCLUDE** is the view's grain minus what you list, so `{EXCLUDE [Sub-Category] : SUM([Sales])}` returns the category total on every sub-category row, which is how you build a percent-of-parent. Rule of thumb: if the number must stay put when the view changes, use FIXED; if it should follow the view, use INCLUDE or EXCLUDE.

code

text · 8 lines
text
// Absolute: each customer's lifetime sales, whatever the view shows
{ FIXED [Customer ID] : SUM([Sales]) }

// Relative, finer: per-customer totals inside each mark
AVG({ INCLUDE [Customer ID] : SUM([Sales]) })

// Relative, coarser: the parent total on every child row
SUM([Sales]) / MIN({ EXCLUDE [Sub-Category] : SUM([Sales]) })

go deeper

for a junior

Learn the shape {FIXED [Dim] : SUM([Measure])} and be able to say which keyword ignores the view. One concrete example per keyword, such as sales per customer, is enough at this level.

for a middle

Explain absolute versus relative grain precisely, including that the view's level of detail counts everything on the Marks card. Give a real use for each keyword and name the outer aggregation you would wrap around the result.

for a senior

Show judgment about which keyword survives future edits to a dashboard, and be ready for the filter interaction: FIXED is computed before dimension filters and will disagree with the rest of the sheet unless you use a context filter or fold the condition into the expression.

for a principal

Decide when a level of detail expression is patching a modelling gap. Logic every workbook re-derives — customer acquisition date, account tier, per-entity totals — is usually cheaper and safer as a modelled column upstream than as a FIXED expression repeated in twenty workbooks.

## Level of detail, in one sentence Every Tableau worksheet has a *view level of detail*: the set of dimensions on Rows, Columns and the Marks card, including anything on Detail or Tooltip. Ordinary aggregations inherit that grain — `SUM([Sales])` produces one number per mark and changes the moment you add a dimension. A level of detail (LOD) expression is an aggregate that declares its own grain instead of inheriting the view's. ## The syntax ``` { FIXED [Dim1], [Dim2] : AGG([Measure]) } ``` The braces make it an LOD. The keyword is FIXED, INCLUDE or EXCLUDE. Between the keyword and the colon is a dimensionality declaration; after the colon is an aggregate expression. The whole thing returns an aggregate value, which the view will then aggregate again — so what you wrap it in matters. ## FIXED — absolute grain FIXED ignores the view's dimensions entirely and computes at exactly the dimensions you list. `{FIXED [Customer ID] : SUM([Sales])}` is each customer's total sales. Drop Region into the view, swap it for Category, remove it altogether — the per-customer number does not move. With no dimensions at all, `{FIXED : SUM([Sales])}` is one number for the entire table, which is the standard building block for a percent-of-total that survives view changes. Because FIXED refuses to look at the view, it also refuses to look at ordinary dimension filters: those are applied after FIXED expressions are computed. Context filters, which are applied before, do affect it. That asymmetry is the single most-asked follow-up on this topic. ## INCLUDE — the view plus something finer INCLUDE computes at the view's dimensions *plus* the ones you name, then hands the result back to the view to be re-aggregated. The canonical use is an average over a grain finer than the view. `AVG({INCLUDE [Customer ID] : SUM([Sales])})` computes each customer's sales inside whatever the mark covers, then averages those per-customer totals. Put Region on Rows and it reads "average sales per customer in this region"; swap Region for Category and it re-reads itself as "average sales per customer in this category" with no edit. That relativity is the point: INCLUDE follows the view. INCLUDE is only meaningful when the dimension you name is finer than, or absent from, the view. Including a dimension already in the view adds nothing. ## EXCLUDE — the view minus something EXCLUDE computes at the view's dimensions *minus* the ones you name. In a view broken out by Category and Sub-Category, `{EXCLUDE [Sub-Category] : SUM([Sales])}` returns the category total on every sub-category row. Divide the mark's own sales by it and you have percent of category — one that keeps working when someone reorders the shelves. Excluding a dimension that is not in the view does nothing at all, which is a frequent silent no-op: the calculation saves, the number looks plausible, and it is simply the ordinary aggregate. ## A worked comparison View: Region on Rows, Category on Columns, `SUM([Sales])` as the measure. ``` Region Category SUM(Sales) {FIXED [Region]} {EXCLUDE [Category]} East Furniture 40 100 100 East Tech 60 100 100 West Furniture 30 80 80 West Tech 50 80 80 ``` Here FIXED and EXCLUDE agree, because removing Category from this particular view leaves exactly Region. Now move Category off the view: the FIXED column is unchanged at 100 and 80, while the EXCLUDE column becomes the same as `SUM([Sales])`, because there is no Category left to remove. Same numbers today, different behaviour tomorrow — that is the difference candidates are being tested on. ## The result is aggregated again An LOD returns an aggregate, and the view still needs one number per mark, so Tableau applies an outer aggregation — SUM by default when you drop it as a measure. `AVG({INCLUDE [Customer ID] : SUM([Sales])})` and `SUM({INCLUDE [Customer ID] : SUM([Sales])})` are both valid and answer completely different questions. Choosing that wrapper deliberately is part of writing an LOD, not an afterthought. ## Nesting and using an LOD as a dimension LOD expressions nest: `{FIXED [Region] : AVG({INCLUDE [Customer ID] : SUM([Sales])})}` is legal and computes the inner expression first. A FIXED expression whose result is a stable per-entity attribute — the classic being `{FIXED [Customer ID] : MIN([Order Date])}`, a customer's acquisition date — can be converted to a dimension and used to group marks, which is how cohort analysis is built in Tableau without touching the source. ## What interviewers listen for They want the one-line contrast (absolute versus relative to the view), a concrete use for each, and the awareness that FIXED sidesteps dimension filters. A strong answer volunteers that the LOD's result is re-aggregated by the view, and that EXCLUDE of a dimension not in the view quietly does nothing.

  • What does {FIXED : SUM([Sales])} with no dimensions before the colon compute?
    A single value for the whole table: total sales across every row that survived the filters applied before FIXED. It is the standard denominator for a percent-of-total that does not shift when someone changes the shelves. Wrap it in MIN, MAX or ATTR rather than SUM when you place it beside a finer mark, so the constant is read back once.
  • Does an INCLUDE expression react to a dimension sitting on the Detail shelf?
    Yes. The view's level of detail is every dimension on the Marks card, not just Rows and Columns, so Detail and Tooltip count. Dropping a dimension onto Detail silently changes what INCLUDE and EXCLUDE compute, and is a common reason a number moves after an apparently cosmetic edit. FIXED is unaffected, because it never consults the view.
  • How would you build a customer cohort analysis with an LOD expression?
    Create `{FIXED [Customer ID] : MIN([Order Date])}` — each customer's first order date, constant regardless of the view. Convert it to a dimension, truncate it to month, and put it on Rows to group customers by acquisition cohort while the measure varies by order month. Because it is FIXED, the cohort label does not shift when the view is filtered to a later period.
  • Can level of detail expressions be nested inside one another?
    Yes. `{FIXED [Region] : AVG({INCLUDE [Customer ID] : SUM([Sales])})}` is valid: the inner expression is evaluated first at its own grain, then the outer one aggregates those results at region level. Keep nesting shallow — each level adds work to the generated query, and a two-deep expression is already hard for the next reader to verify.

saying these in an interview costs you the question

  • Describes FIXED as merely a faster GROUP BY of the view
  • Thinks INCLUDE and EXCLUDE also ignore the view's dimensions
  • Excludes a dimension that is not in the view and expects a change
  • Forgets the LOD result is aggregated again by the view
  • Reaches for FIXED whenever any level of detail expression is needed

context