skip to content

In Tableau, when does a table calculation such as RUNNING_SUM actually compute?

level: middleimportance: should knowfreq 66%

answer

  1. it happens after the database is done
  2. it can only see what the view returned
  3. direction and restart are separate settings
  4. filtering earlier months changes the opening balance

basics

~20 s

Table calculations run inside Tableau on the aggregated result the data source already returned, after filters, level of detail expressions and aggregation. They can only see marks present in the view, and their direction depends on addressing and partitioning.

solid answer

~50 s

Row-level and level of detail calculations are computed by the data source in the query Tableau sends. A **table calculation** like `RUNNING_SUM(SUM([Sales]))` or `WINDOW_SUM(SUM([Sales]), -2, 0)` is computed afterwards, locally, over the aggregated result set — which is why its argument must already be an aggregate. Two consequences matter. First, it can only see what reached the view: any row removed by a dimension or measure filter simply does not exist, so a running total after filtering to Q4 starts at Q4, not at the year's opening balance. Second, its result depends on **addressing and partitioning**: the dimensions you compute along set the direction, and every other dimension in the view partitions the calculation so it restarts. Change the shelves and the same formula returns different numbers — the calculation is a property of the table's shape, not just of the data.

code

text · 8 lines
text
// Legal: the argument is already an aggregate
RUNNING_SUM(SUM([Sales]))

// Trailing three marks including the current one
WINDOW_SUM(SUM([Sales]), -2, 0)

// Illegal at this stage: no row-level field survives the query
RUNNING_SUM([Sales])

go deeper

for a junior

Know that these functions run after the query and operate on the values already shown in the view, and that they need an aggregate argument such as SUM([Sales]) rather than a bare column.

for a middle

Explain addressing versus partitioning and predict what happens to a running total when a dimension is added to the view. Be able to say why filtering to one quarter changes the opening balance of a cumulative figure.

for a senior

Demonstrate the diagnostic habit: when a percent-of-total reads 100% everywhere or a total resets, you check Compute Using and the view's dimensions first. Know the late-filter technique for trimming a view without recomputing, and when a level of detail expression is the right tool instead.

for a principal

Weigh maintainability. Table calculations are coupled to the shape of a specific worksheet, so heavy reliance on them makes dashboards fragile under edit; recurring cumulative or period-over-period logic is often better modelled upstream where every consumer inherits the same definition.

## Three places a calculation can run Tableau evaluates calculations at three different moments, and knowing which one you are in explains most surprising numbers: - **Row-level** (`[Sales] - [Cost]`) — in the data source, per underlying row. - **Aggregate and level of detail** (`SUM([Sales])`, `{FIXED [Region] : SUM([Sales])}`) — in the query, at the view's grain or the grain the LOD declares. - **Table calculation** (`RUNNING_SUM`, `WINDOW_SUM`, `INDEX`, `RANK`, `LOOKUP`) — in Tableau, after the query has returned, over the aggregated result. A table calculation is therefore the last thing to happen. The data source never sees it, and it can never reach back into detail the query did not return. ## What it operates on Once the query returns, Tableau holds a small table of aggregated values — one row per mark. That table is what a table calculation reads. This is why every table calculation function takes an aggregate as its argument: `RUNNING_SUM(SUM([Sales]))` is well formed, `RUNNING_SUM([Sales])` is not, because there is no bare `[Sales]` left at this point. ``` RUNNING_SUM(SUM([Sales])) -- cumulative to the current mark WINDOW_SUM(SUM([Sales]), -2, 0) -- trailing three marks, inclusive LOOKUP(SUM([Sales]), -1) -- the previous mark's value INDEX() -- position of the mark within its partition ``` ## Addressing and partitioning Every dimension in the view is either **addressing** or **partitioning** for a given table calculation. Addressing dimensions define the direction the calculation moves along; partitioning dimensions carve the table into independent groups, and the calculation restarts in each one. In the interface this is the Compute Using setting — Table, Pane, Cell, or a specific list of dimensions. A running total of monthly sales in a view of Year and Month, addressed by Month only, restarts every January because Year partitions it. Addressed by both Year and Month, it runs continuously across the whole table. The formula is identical in both cases; only the addressing differs. Setting Compute Using to specific dimensions rather than the positional defaults (Table across, Pane down) is what keeps a calculation correct after someone reorders the shelves. ## The invisible-data problem Because the calculation runs on what came back, anything filtered out is gone. Filter a view to the last quarter and `RUNNING_SUM(SUM([Sales]))` starts from zero at the beginning of that quarter — it cannot include January, because January never arrived. Similarly, a table calculation cannot compute a distinct count across data outside the view, or reference a dimension that is not in the view at all: it can only address dimensions that are present, including those parked on Detail. When you need a number computed over data the view does not show, that is a level of detail expression's job, not a table calculation's. `{FIXED [Customer ID] : SUM([Sales])}` is answered by the data source over the whole table; `WINDOW_SUM` can only add up what is on screen. ## Filters on table calculations run last of all There is one useful exception to "filtering changes the calculation". A filter placed on a *table calculation* is applied after the table calculations are computed, so it hides marks without recomputing anything. That is the standard way to show only the last six months of a running total while keeping the total correct: build the running sum over the full period, then filter on a table calculation such as `LAST() <= 5` to trim the display. A plain dimension filter on the date would instead remove the rows and reset the running total. ## Symptoms to recognise - A percent-of-total that always shows 100% — the calculation is partitioned by the very dimension it should be spanning. - A running total that resets unexpectedly — an extra dimension in the view has become a partition. - A number that changes when someone swaps Rows and Columns — the calculation is using a positional Compute Using rather than named dimensions. - A cumulative figure that starts at the wrong opening balance — a dimension filter removed earlier periods before the calculation ran. ## What interviewers listen for They want you to place table calculations last in the pipeline and to explain the practical consequence: they see only what the view returned, and their result depends on the table's shape through addressing and partitioning. Naming the late-filter trick, and knowing when to reach for a level of detail expression instead, is what separates a confident answer from a memorised one.

  • Why does a running total restart when someone adds a dimension to the view?
    Any dimension in the view that is not part of the calculation's addressing becomes a partition, and the calculation restarts inside each partition. Adding Year to a monthly view makes the running sum reset every January unless you set Compute Using to the specific dimensions you want to move along. Naming dimensions explicitly, rather than relying on Table across or Pane down, keeps the result stable when the layout changes.
  • How do you show only the last six months of a running total without breaking it?
    Compute the running total over the full period, then filter with a filter on a table calculation, for example one built on `LAST()`. Filters on table calculations are applied after the calculations run, so they hide marks without recomputing them. A plain dimension filter on the date would remove the earlier rows before the calculation ran, and the running total would restart at the new first month.
  • When should you reach for a level of detail expression instead of a table calculation?
    When the number depends on data the view does not display. A table calculation only sees the aggregated marks that came back, so it cannot count distinct customers across the whole table or fetch a per-customer lifetime value while the view shows regions. `{FIXED [Customer ID] : SUM([Sales])}` is computed by the data source over everything, which is exactly what a table calculation cannot do.

saying these in an interview costs you the question

  • Believes RUNNING_SUM is executed by the database
  • Expects a table calculation to see rows a filter removed
  • Leaves Compute Using on Table across without checking partitions
  • Uses a table calculation where a FIXED expression is required
  • Passes a raw column to a table calculation function

context