Historical Context & Motivation
Business intelligence tools have long struggled with a fundamental tension: how do you display data at one granularity while computing metrics at another? Early SQL-based reporting tools forced analysts to write complex subqueries or correlated aggregations whenever they needed, say, a per-customer total displayed alongside a per-order breakdown. This tension became especially pronounced as self-service analytics platforms rose in popularity during the 2000s. Business users wanted drag-and-drop simplicity, but the underlying data models often demanded multi-level aggregation logic that only SQL experts could craft efficiently.
The core question that LOD expressions answer is deceptively simple: How can I compute an aggregate at a specific entity level—say, per customer—and then use that value in a visualization that operates at a completely different grain, such as per region or per month? Before LOD expressions, the most common workarounds involved pre-aggregated data sources, complex data blending, or nested SQL. The FIXED LOD expression eliminates these workarounds by letting the analyst specify the computation grain declaratively, inline, within a calculated field.
Core Principles & Definitions
To understand FIXED LOD, you need to internalize how Tableau's query pipeline works. When you place dimensions and measures on a shelf, Tableau constructs a query whose level of detail (LOD) is determined by the set of dimensions in the view. A FIXED LOD expression overrides this default behavior by specifying its own set of dimensions, independent of what is on the viz shelves. This decoupling is the foundational insight that makes per-entity metrics possible.
Declarative Grain Specification
{ FIXED [Customer ID] : SUM([Sales]) } tells Tableau to compute total sales per customer, no matter what dimensions appear in the visualization.Independence from Viz Filters
Reusable Scalar per Entity
Contrast with INCLUDE and EXCLUDE
{ FIXED [Customer ID] : SUM([Sales]) } constructs a dictionary where each key is a customer and each value is their aggregate. Tableau then joins this dictionary back to every row, so any visualization—regardless of its own granularity—can reference the per-customer metric.Visual Explanation — How FIXED LOD Decouples Grain
Notice how the raw data contains six rows across three customers, yet the FIXED expression collapses this to exactly three values—one per customer. When the visualization groups by Region, Tableau further aggregates those three customer-level totals using whatever outer aggregation you specify (AVG, SUM, MIN, etc.). This two-stage aggregation is the defining characteristic of FIXED LOD: the inner aggregation is locked to the entity you declare, while the outer aggregation adapts to whatever the visualization requires. If you think of this in relational algebra terms, the FIXED expression is essentially a GROUP BY on the specified dimension followed by a natural join back to the original relation, after which the viz applies its own GROUP BY.
Syntax, Semantics & Execution Model
FIXED LOD Syntax
[dim₁], [dim₂], … are the dimensions defining the computation grain, AGG is any Tableau aggregate function (SUM, AVG, MIN, MAX, COUNTD, etc.), and [measure] is the field being aggregated. The curly braces are mandatory and signal an LOD expression to the Tableau parser.SELECT customer_id, SUM(sales) FROM orders GROUP BY customer_id — the result is then joined back to every row sharing that customer_id.Outer Aggregation Requirement
A subtle but critical point: when the FIXED dimension is at a finer grain than the viz, Tableau must further aggregate the FIXED result to match the viz level. For instance, if your viz shows regions and your FIXED field returns per-customer totals, Tableau wraps the FIXED expression in an outer aggregation—AVG({ FIXED [Customer ID] : SUM([Sales]) }) would give the average customer spend per region. Conversely, when the FIXED dimension is at a coarser grain than the viz, the single FIXED value is replicated across all finer rows—analogous to a broadcast join in distributed computing.
{ FIXED : SUM([Sales]) }—computes the grand total across the entire data source. This is equivalent to a SQL SELECT SUM(sales) FROM orders with no GROUP BY. It produces a single scalar replicated to every row, making it useful for percent-of-total calculations: SUM([Sales]) / { FIXED : SUM([Sales]) }.LOD Expression Types — FIXED vs. INCLUDE vs. EXCLUDE
Tableau provides three LOD expression types, each with a distinct relationship to the dimensions present in the visualization. Understanding these differences is essential for choosing the right tool. The following diagram and table provide a comprehensive comparison, using a data set where the viz-level dimensions are [Region] and [Category].
| Property | FIXED | INCLUDE | EXCLUDE |
|---|---|---|---|
| Grain relationship | Absolute — independent of viz | Relative — viz grain + extra dims | Relative — viz grain − specified dims |
| Affected by dim filters? | No (unless added to context) | Yes | Yes |
| SQL analogy | Subquery with independent GROUP BY | Adding column to main GROUP BY | Removing column from main GROUP BY |
| Typical use case | Per-customer total, cohort assignment, grand totals | AVG of per-customer order sizes | Region-level total ignoring sub-category |
Worked Example — Average Customer Spend by Region
Suppose you are working with Tableau's sample Superstore data set and you need to build a bar chart showing the average total spend per customer in each region. Without FIXED LOD, Tableau would compute AVG([Sales]) at the row level, giving you the average transaction amount—not the average customer spend. The FIXED expression solves this by first computing each customer's total, then averaging those totals per region.
Customer Total Sales with the formula: { FIXED [Customer ID] : SUM([Sales]) }. This computes the sum of sales for each unique Customer ID across the entire data source. In SQL terms, this is a correlated scalar subquery: SELECT SUM(sales) FROM orders o2 WHERE o2.customer_id = o1.customer_id joined back to each row.[Region] to the Columns shelf. This sets the visualization grain to region level. Tableau will group data into four regions: Central, East, South, and West.Customer Total Sales to the Rows shelf. Tableau will default to SUM as the outer aggregation, which sums all customer totals within each region—that gives you regional revenue, not average customer spend. Change the aggregation to AVG by right-clicking the pill and selecting Measure → Average. The resulting expression is effectively: AVG({ FIXED [Customer ID] : SUM([Sales]) }).Strengths, Limitations & Common Pitfalls
| Strengths | Limitations |
|---|---|
| Decouples computation grain from viz grain, enabling per-entity KPIs in any view. | Ignores dimension filters by default, which can produce unexpected results for users unfamiliar with the order of operations. |
| Produces deterministic, stable values — the same customer always gets the same total regardless of filters. | Cannot reference parameters inside the dimension list (only in the aggregate expression). You cannot write { FIXED [Parameter] : SUM([Sales]) }. |
| Can be nested inside other calculations, used in IF/THEN logic, and combined with table calculations for sophisticated analytics. | May generate a more complex underlying query, potentially impacting performance on large data sets if the FIXED dimension has very high cardinality. |
| Supports multiple dimensions in a single expression, enabling multi-key entity definitions (e.g., per customer per year). | Results are not cached across worksheets — each sheet re-evaluates the expression, though Tableau's query optimizer often consolidates identical subqueries. |
Connection to Advanced LOD Patterns
Once you are comfortable with single-dimension FIXED expressions, a world of advanced patterns opens up. These patterns are ubiquitous in production Tableau dashboards at enterprise scale, and understanding them deepens your ability to model complex business logic declaratively.
| Pattern | Expression | Use Case |
|---|---|---|
| Cohort Assignment | { FIXED [Customer ID] : MIN([Order Date]) } | Assigns each customer to their first-purchase cohort for retention analysis. |
| Nested FIXED | { FIXED [Region] : AVG({ FIXED [Customer ID] : SUM([Sales]) }) } | Computes per-customer totals, then averages them per region in a single calculated field. |
| Boolean Bucketing | IF { FIXED [Customer ID] : SUM([Sales]) } > 500 THEN 'High' ELSE 'Low' END | Segments customers into tiers based on total spend — usable as a dimension for color or filtering. |
| Percent of Entity Total | SUM([Sales]) / { FIXED [Customer ID] : SUM([Sales]) } | Shows what fraction of a customer's total spend each individual order or category represents. |
| Grand Total Reference | SUM([Sales]) / { FIXED : SUM([Sales]) } | Computes each row or group as a percentage of the overall total. The empty FIXED returns a single scalar. |
These patterns can be combined with table calculations for even more powerful analytics. For example, you might use a FIXED expression to compute each customer's first purchase date (cohort assignment), then use a table calculation like RUNNING_SUM to track cumulative retention over time. The key architectural insight is that FIXED LOD expressions are evaluated in Tableau's query phase (sent to the database), while table calculations execute in a post-query phase (computed in Tableau's memory). Understanding this pipeline is analogous to understanding the difference between compile-time and runtime evaluation in a compiled language.
Practice Problems
AVG([Sales]) computed at the viz level (with [Region] on Rows) gives a different result than AVG({ FIXED [Customer ID] : SUM([Sales]) }) at the same viz level. What fundamental difference in granularity accounts for the discrepancy?Lesson Summary
FIXED LOD expressions solve the fundamental problem of computing per-entity metrics (such as per-customer total sales, per-product margin, or per-employee performance) at a grain that is independent of the visualization's level of detail. The syntax { FIXED [dimension] : AGG([measure]) } declares an absolute computation grain, producing a scalar value per unique combination of the specified dimensions. These values are then available for further aggregation (AVG, MAX, COUNTD, etc.) at whatever grain the visualization requires.
Key architectural considerations include the order of operations (FIXED evaluates before dimension filters, requiring context filters for filter interaction), the distinction from INCLUDE and EXCLUDE (which are relative to the viz grain rather than absolute), and the outer aggregation requirement when the FIXED grain is finer than the viz grain. Advanced patterns—cohort assignment, nested FIXED, boolean bucketing, and percent-of-entity calculations—extend this foundation to handle complex, real-world analytics scenarios.