Table Calculations in Tableau
Table calculations are one of Tableau's most powerful — and most misunderstood — features. They operate on the result set of a query, not on the underlying rows of data. This means they can answer questions that neither row-level calculations nor aggregate calculations can: "What is the cumulative total so far?", "How does this month compare to the previous month?", "What rank does this product hold within its category?"
This chapter explains what table calculations are, how Tableau applies them, and how to use every major function in the table calculation library.
What Are Table Calculations?
When Tableau executes a view, it follows a pipeline:
- Row-level calculations — computed for each row of raw data (e.g.,
[Sales] * 1.1) - Aggregation — raw rows are grouped by the dimensions in the view and aggregated (e.g.,
SUM([Sales])) - Table calculations — computed on the aggregated result set that Tableau already has in memory
This position in the pipeline gives table calculations two defining characteristics:
They see aggregated values, not raw rows. A table calculation cannot access individual order values; it sees only the SUM(Sales) per region, or COUNT(Orders) per month, as already computed.
They run entirely in Tableau's engine, not in the database. Unlike a measure filter that can be pushed down to a SQL HAVING clause, table calculations execute in Tableau's local compute layer. This means all data required for the calculation must already be in Tableau's result set, which has performance implications.
How Table Calculations Differ
| Calculation Type | Runs On | Example |
|---|---|---|
| Row-level | Each raw data row | [Profit] / [Sales] |
| Aggregate | Groups of rows | SUM([Sales]) |
| Table Calculation | Aggregated result set | RUNNING_SUM(SUM([Sales])) |
Quick Table Calculations
The fastest way to create a table calculation is to right-click a measure pill on a shelf and choose Quick Table Calculation. Tableau offers the following presets:
Running Total
Accumulates the measure from the first value to the current value along the specified direction.
Example: Month-by-month cumulative sales. January shows January's total; February shows January + February; March shows the sum of all three months so far.
Difference
Subtracts the previous value from the current value.
Example: How much did sales change from last month to this month? If January = 50,000 and February = 58,000, the difference for February = 8,000.
Percent Difference
The Difference expressed as a percentage of the previous value.
Formula equivalent: (Current - Previous) / ABS(Previous)
Example: A month-over-month growth rate. If January = 50,000 and February = 58,000, the percent difference = 16%.
Percent of Total
Each value divided by the total of all values in the addressing direction.
Example: What percentage of annual sales occurred in each month? February's contribution = February Sales / Full Year Sales.
Rank
Assigns a rank to each value (1 = highest by default). Values that are tied receive the same rank, and the next rank is skipped (standard competition ranking: 1, 1, 3, 4...).
Percentile
Shows what percentile each value falls into relative to the others in the partition.
Moving Average
Averages a rolling window of N values centered on (or ending at) the current value. Smooths out noise and reveals trends.
Default window: The previous 2, current, and next 2 values (a 5-value window).
YTD Total
Sums values from the beginning of the year to the current date. Requires a date dimension in the view.
Compound Growth Rate
Calculates the compound annual growth rate (CAGR) from the first to the last value.
Formula: (Last / First) ^ (1 / (N - 1)) - 1
Year over Year Growth
Compares the current period to the same period in the prior year.
YTD Growth
Combines YTD Total with Year over Year comparison.
Addressing and Partitioning
These two concepts govern where a table calculation starts, stops, and restarts. They are the most conceptually challenging aspect of table calculations, and mastering them is what separates intermediate from advanced Tableau users.
The Mental Model
Imagine a table of values in your view. A table calculation moves through those values in some direction, accumulating or comparing as it goes. That direction is the addressing. The point where it resets and starts over is the partitioning.
- Addressing — the dimension(s) across which the calculation moves (the "direction of computation")
- Partitioning — the dimension(s) that restart the calculation from scratch
Every dimension in the view is either addressing or partitioning for a given table calculation. They cannot be both.
Built-in Addressing Options
When you right-click a table calculation and choose Edit Table Calculation, Tableau presents a dropdown:
| Option | Meaning |
|---|---|
| Table (across) | Moves left to right across the entire table |
| Table (down) | Moves top to bottom across the entire table |
| Table (across then down) | Across each row, then to the next row |
| Table (down then across) | Down each column, then to the next column |
| Pane (across) | Same as Table (across) but restarts at each pane boundary |
| Pane (down) | Same as Table (down) but restarts at each pane |
| Cell | Computes within each cell individually (rarely useful) |
| Specific Dimensions | Manual control — choose which dimensions are addressing |
Visual Example: Running Total Across vs. Down
Suppose your view has months as columns (Jan, Feb, Mar) and product categories as rows (Furniture, Office Supplies, Technology).
Table (across): The running total moves Jan → Feb → Mar within each row. Jan = Jan, Feb = Jan+Feb, Mar = Jan+Feb+Mar. Then it resets for the next row.
Table (down): The running total moves Furniture → Office Supplies → Technology within each column. The Jan column shows: Furniture Jan, then Furniture+Office Jan, then all three categories combined for Jan.
Table (across then down): Moves across all of January first, then wraps around to February, treating the entire table as one long sequence.
Specific Dimensions: Manual Control
The most powerful option is "Specific Dimensions." You check or uncheck each dimension in the view to manually designate which are addressing (checked) and which are partitioning (unchecked).
Example: You have [Year] and [Month] as columns, [Category] as rows. You want a running total that:
- Moves across months (Month is addressing — checked)
- Resets for each year (Year is partitioning — unchecked)
- Resets for each category (Category is partitioning — unchecked)
Check only [MONTH(Order Date)]. The calculation moves January through December within each Year × Category combination and resets when either changes.
The WINDOW Functions
WINDOW functions compute an aggregate over a specified range of values within the partition. Unlike RUNNING functions (which accumulate from the start), WINDOW functions can look both backward and forward.
Syntax
WINDOW_AGG(expression, [start, end])
expression— the measure (already aggregated in the view)start— offset from the current value. 0 = current row. Negative = backward. FIRST() = first row of partition.end— same offset rules. LAST() = last row of partition.
Omitting start and end means the window spans the entire partition.
WINDOW_SUM
Returns the sum of a measure over the window.
WINDOW_SUM(SUM([Sales]))
With no range arguments, returns the total sales across the entire partition — effectively the same as the grand total. Useful for computing "percent of total" inline:
SUM([Sales]) / WINDOW_SUM(SUM([Sales]))
WINDOW_AVG
Returns the average over the window.
WINDOW_AVG(SUM([Sales]), -2, 0)
Returns the average of the current month and the two preceding months — a 3-month trailing average.
WINDOW_COUNT
Returns the count of non-null values in the window.
WINDOW_COUNT(SUM([Sales]))
WINDOW_MAX and WINDOW_MIN
Return the maximum and minimum values in the window.
WINDOW_MAX(SUM([Sales]))
Useful for computing "percent of the peak": SUM([Sales]) / WINDOW_MAX(SUM([Sales]))
WINDOW_MEDIAN
Returns the median value in the window. Unlike WINDOW_AVG, the median is resistant to outliers.
WINDOW_MEDIAN(SUM([Sales]))
WINDOW_PERCENTILE
Returns the value at a specified percentile within the window.
WINDOW_PERCENTILE(SUM([Sales]), 0.75)
Returns the 75th percentile value of SUM(Sales) across the partition.
WINDOW_STDEV and WINDOW_VAR
Return the standard deviation and variance, respectively, of the values in the window.
WINDOW_STDEV(SUM([Sales]))
WINDOW_VAR(SUM([Sales]))
Useful for statistical analysis, control charts, and detecting outliers.
The LOOKUP Function
LOOKUP accesses the value of an expression at a relative position from the current row.
Syntax
LOOKUP(expression, [offset])
expression— the measure to look upoffset— the number of rows to jump. Negative = backward, positive = forward, 0 = current row.
Common Uses
Previous value comparison:
LOOKUP(SUM([Sales]), -1)
Returns the sales value from the row before the current row. Combine with the current value to compute a difference:
SUM([Sales]) - LOOKUP(SUM([Sales]), -1)
Next value preview:
LOOKUP(SUM([Sales]), 1)
First value in the partition:
LOOKUP(SUM([Sales]), FIRST())
Last value in the partition:
LOOKUP(SUM([Sales]), LAST())
LOOKUP vs. WINDOW Functions
LOOKUP retrieves a single value at a specific offset. WINDOW_AGG computes an aggregate across a range. Use LOOKUP when you need to compare the current value to a specific single other value. Use WINDOW functions when you need a summary statistic across multiple values.
PREVIOUS_VALUE
PREVIOUS_VALUE returns the result of the table calculation from the previous row, or a seed value if it is the first row.
Syntax
PREVIOUS_VALUE(seed_value)
This function is used to build calculations that accumulate in non-standard ways, where you need each step to reference the result of the step before it (not just the data of the previous row).
Example: Custom compounding:
PREVIOUS_VALUE(1) * (1 + SUM([Growth Rate]))
Starts at 1, then multiplies each step by (1 + the period's growth rate), producing a compounded growth index.
RUNNING Functions
The RUNNING family of functions accumulates a value from the beginning of the partition to the current row.
RUNNING_SUM
RUNNING_SUM(SUM([Sales]))
The cumulative sum of Sales from the first row to the current row. This is the most commonly used running function.
RUNNING_AVG
RUNNING_AVG(SUM([Sales]))
The average of all values from the first row to the current row. Equivalent to RUNNING_SUM / RUNNING_COUNT but computed directly.
RUNNING_COUNT
RUNNING_COUNT(SUM([Sales]))
The count of non-null values from the start to the current row.
RUNNING_MAX and RUNNING_MIN
RUNNING_MAX(SUM([Sales]))
RUNNING_MIN(SUM([Sales]))
The maximum and minimum values seen so far. RUNNING_MAX always increases or stays flat. RUNNING_MIN always decreases or stays flat.
RANK Functions
Tableau provides five rank functions, each handling ties differently:
| Function | Tie Handling | Example (values: 100, 100, 80) |
|---|---|---|
| RANK | Standard competition (1, 1, 3) | Tied items share rank; next rank skipped |
| RANK_DENSE | Dense ranking (1, 1, 2) | Tied items share rank; no rank skipped |
| RANK_MODIFIED | Modified competition (2, 2, 3) | Tied items get the last rank they would occupy |
| RANK_UNIQUE | Unique rank (1, 2, 3) | No ties; order is determined by row order |
| RANK_PERCENTILE | Percentile (0.0 to 1.0) | Where 1.0 is the highest |
Syntax
RANK(SUM([Sales]))
RANK(SUM([Sales]), 'asc')
The second argument controls sort order: 'asc' gives rank 1 to the smallest value, 'desc' (default) gives rank 1 to the largest.
Using RANK to Sort in Creative Ways
You can use RANK as a calculated field and then sort your dimension by that field. This lets you rank products within each category independently — something a standard sort cannot do without a workaround.
INDEX() and SIZE()
INDEX()
Returns the position of the current row within the partition, starting at 1.
INDEX()
| Row | Value | INDEX() |
|---|---|---|
| January | 50000 | 1 |
| February | 58000 | 2 |
| March | 45000 | 3 |
Uses:
- Show row numbers in a table
- Alternate background colors (INDEX() % 2 = 0)
- Reference the first or last row (INDEX() = 1, INDEX() = SIZE())
SIZE()
Returns the total number of rows in the current partition.
SIZE()
If the partition has 12 months, SIZE() returns 12 for every row. Combined with INDEX(), you can determine the midpoint: INDEX() = INT(SIZE() / 2).
FIRST() and LAST()
FIRST()
Returns the offset from the current row to the first row of the partition. At the first row, FIRST() = 0. At the second row, FIRST() = -1. At the Nth row, FIRST() = -(N-1).
Negative values indicate the first row is "behind" the current row.
Use with LOOKUP:
LOOKUP(SUM([Sales]), FIRST())
Always retrieves the value from the first row, regardless of the current position.
LAST()
Returns the offset from the current row to the last row. At the last row, LAST() = 0. At the second-to-last row, LAST() = 1.
Use with LOOKUP:
LOOKUP(SUM([Sales]), LAST())
Always retrieves the value from the last row — useful for computing "percent of the final period" or comparing each value to the most recent result.
Nesting Table Calculations
A table calculation can reference another table calculation. This is called nesting.
Example: Rank of Running Total
First, a running total:
RUNNING_SUM(SUM([Sales]))
Then, rank by the running total:
RANK(RUNNING_SUM(SUM([Sales])))
Each nested calculation can have its own addressing and partitioning settings, configured independently in the Edit Table Calculation dialog by clicking the inner calculation's name in the "Nested Calculations" dropdown.
Nested table calculations are useful but complex — they can be difficult to debug and slow on large datasets. Use them only when a single table calculation cannot accomplish the goal.
Common Use Cases
1. Running Total of Sales by Month
Build a line chart with MONTH(Order Date) on Columns and SUM([Sales]) on Rows. Right-click SUM(Sales) → Quick Table Calculation → Running Total. The line now shows cumulative sales rather than monthly sales, making it easy to see when you hit revenue milestones.
2. Month-over-Month Percentage Change
Use Percent Difference: right-click SUM(Sales) → Quick Table Calculation → Percent Difference. This shows how much each month grew or shrank compared to the prior month. Add a reference line at 0 to visually separate months of growth from months of decline.
3. Ranking Products Within Each Category
Create a calculated field:
RANK(SUM([Sales]))
In the Compute Using settings, set addressing to [Product Name] and partitioning to [Category]. Now each product's rank is computed within its category, not globally. Products in different categories can share the same rank number.
4. Percent of Total Contribution
Add SUM([Sales]) to your view, then apply Percent of Total as a Quick Table Calculation. Alternatively, create a calculated field:
SUM([Sales]) / WINDOW_SUM(SUM([Sales]))
Format as a percentage. This shows each segment's share of the total, with the denominator being the full WINDOW_SUM regardless of the current row.
5. Moving 3-Month Average
Create a calculated field:
WINDOW_AVG(SUM([Sales]), -2, 0)
This averages the current month and the two preceding months. Add this as a secondary line on a monthly sales line chart to show the smoothed trend alongside the raw monthly data. It effectively filters out noise from unusual single months.
Practice Exercises
Exercise 1: Cumulative Revenue Line Chart
Dataset: Sample - Superstore
Task:
- Create a line chart with MONTH(Order Date) on Columns, SUM(Sales) on Rows.
- Duplicate the SUM(Sales) pill on Rows (Ctrl+drag) to create a dual-axis.
- On the second axis, apply a Running Total quick table calculation.
- Right-click the second axis → Synchronize Axis → then uncheck to leave it independent.
- Format the Running Total line differently (thicker, different color).
- Add a reference line on the Running Total axis at Y = 1,000,000 labeled "1M Milestone."
Goal: Display both monthly actuals and cumulative revenue on the same chart.
Exercise 2: Month-over-Month Growth Rate with Color
Dataset: Sample - Superstore
Task:
- Create a bar chart with MONTH(Order Date) on Columns, SUM(Sales) on Rows.
- Apply Percent Difference as a Quick Table Calculation.
- Drag the same table calculation pill to the Color mark.
- Edit the color to a diverging palette: red for negative growth, green for positive growth.
- Add a reference line at 0%.
- Add data labels showing the percentage change.
Goal: Build an MoM growth chart with intuitive color coding.
Exercise 3: Category-Level Product Rankings
Dataset: Sample - Superstore
Task:
- Build a text table with [Category] and [Sub-Category] on Rows, SUM(Sales) on Text.
- Create a calculated field:
RANK(SUM([Sales])) - Set its Compute Using: Specific Dimensions → check [Sub-Category] only.
- Add this rank field to the view alongside Sales.
- Sort the table by Category, then by rank within each category.
- Verify: Technology's sub-categories should have ranks 1, 2, 3... independently from Furniture's ranks.
Goal: Demonstrate partitioned ranking (rank within group).
Exercise 4: 3-Month Moving Average vs. Actuals
Dataset: Sample - Superstore
Task:
- Create a line chart of SUM(Sales) by MONTH(Order Date).
- Create a calculated field called "3-Month Moving Avg":
WINDOW_AVG(SUM([Sales]), -2, 0) - Add this field to the Rows shelf to create a dual-axis chart.
- Synchronize the axes.
- Format the moving average line: make it thicker and a muted color.
- Format the actual sales line: thinner, lighter, with circular mark shapes at each point.
- Add a legend and title.
Goal: Combine raw data and a smoothed trend in a single, readable chart.
Summary
In this chapter you learned:
-
What table calculations are — computations that run on Tableau's aggregated result set, after the database query returns, allowing cross-row comparisons and accumulations that SQL alone cannot provide.
-
Quick Table Calculations — one-click shortcuts for the most common patterns: Running Total, Difference, Percent Difference, Percent of Total, Rank, Percentile, Moving Average, YTD Total, Compound Growth Rate, YoY Growth, and YTD Growth.
-
Addressing and Partitioning — the two axes of every table calculation. Addressing defines the direction of movement; partitioning defines where the calculation resets. Mastering Specific Dimensions gives you full control over both.
-
WINDOW Functions — compute aggregates (sum, avg, count, max, min, median, percentile, stdev, var) over a defined window of rows, with flexible start and end offsets relative to the current row.
-
LOOKUP — retrieves a value from a relative position (previous, next, first, last) within the partition.
-
PREVIOUS_VALUE — self-referential accumulation that seeds from a value and compounds through the partition.
-
RUNNING functions — accumulate sum, average, count, max, or min from the beginning of the partition to the current row.
-
RANK functions — five variants (RANK, RANK_DENSE, RANK_MODIFIED, RANK_UNIQUE, RANK_PERCENTILE) with different tie-handling behaviors.
-
INDEX() and SIZE() — the current row's position and the total count of rows in the partition, enabling row-number displays and conditional logic.
-
FIRST() and LAST() — the offset distance to the first or last row, used as arguments to LOOKUP for always-first and always-last value retrieval.
-
Nesting — table calculations can be nested inside other table calculations, each with independent addressing and partitioning.
-
Common Patterns — running totals, MoM growth rates, within-group ranks, percent-of-total, and moving averages are the bread-and-butter applications of table calculations in real dashboards.
Table calculations represent Tableau's most expressive analytical layer. Combined with parameters and LOD expressions, they enable you to answer virtually any question about your data directly within the visualization layer.