Home › Data Analytics › Tableau Can't Mix Aggregate and Non-Aggregate Error
Beginner 7 min · September 23, 2026

Tableau Can't Mix Aggregate and Non-Aggregate Error

Wrap row-level fields with ATTR() to fix Tableau's aggregate mismatch.

N
Naren Founder & Principal Engineer

20+ years shipping production backend systems. Notes here come from systems that actually shipped.

Follow
✓ Production
production tested
September 27, 2026
last updated
2,085
articles · all by Naren
Before you start⏱ 12 min
  • ✓A Tableau workbook with a sample data source connected
  • ✓Comfort building basic views with Rows, Columns, and Marks
  • ✓Rough idea of SUM versus row-level fields in any reporting tool
 ● Production Incident 🔎 Debug Guide
⚡Quick Answer
  • Tableau throws this error when one calculation mixes row-level fields with aggregated values such as SUM([Sales]) in a single expression
  • Fix the classic case fast: push the aggregation inside the test with SUM(IF [Region] = "West" THEN [Sales] END) so every branch returns the same granularity
  • Wrap a bare dimension with ATTR([Field]), MIN([Field]), or MAX([Field]) when the view's level of detail guarantees a single value
  • Reach for a FIXED LOD such as { FIXED [Customer ID] : SUM([Sales]) } when you truly need row-level detail and view-level totals in one calc
✦ Definition~90s read
What is Tableau Cannot Mix Aggregate and Non-Aggregate Arguments?

Tableau is a visual analytics platform where you build views by dragging dimensions (blue pills that slice data) and measures (green pills that aggregate it). Under every drag-and-drop, Tableau compiles your view into a query — usually SQL with a GROUP BY — at a specific level of detail determined by the dimensions on the shelf.

★
Picture a teacher who must give one grade per student but is handed a rule that says 'if the student is in Class B, use the whole school's average score.' That's two different levels of detail in one rule: one student versus the entire school.

A calculated field is your escape into custom logic, written in Tableau's formula language with functions like IF, SUM, ATTR, and Level of Detail expressions.

Aggregation is the fault line. Functions like SUM, AVG, COUNTD, MIN, MAX, and ATTR collapse many rows into one value per partition, while bare fields like [Region] or [Order Date] stay at row level. Tableau requires every function inside a single calculation to share one level.

Mix them — IF [Region] = "West" THEN SUM([Sales]) END — and compilation fails before any data moves, because no single GROUP BY can serve both a row test and a partition total.

ATTR() exists as Tableau's compromise aggregator: it returns the value when a partition agrees and '*' when it doesn't. Level of Detail expressions go further, letting you declare an independent level — { FIXED [Customer ID] : SUM([Sales]) } — that survives any view layout.

Together with conditional aggregation (SUM around an IF instead of IF around a SUM), these three tools resolve every legitimate version of this error. The skill isn't memorizing the error text; it's seeing granularity the way Tableau's compiler sees it.

Plain-English First

Picture a teacher who must give one grade per student but is handed a rule that says 'if the student is in Class B, use the whole school's average score.' That's two different levels of detail in one rule: one student versus the entire school. Tableau refuses to guess which level you meant, so it throws the aggregate mismatch error. The fix is to pick one level per calculation. Either compare students to students, or compare school averages to school averages — never both in the same breath.

It's Monday morning and your sales dashboard shows a red calculation error instead of numbers. The formula looks harmless: IF [Region] = "West" THEN SUM([Sales]) END. You tested each half separately and both worked. Together they explode, and the error message — cannot mix aggregate and non-aggregate arguments — reads like it was written for a compiler, not a human.

This error is Tableau's most common calculation complaint, and it confuses beginners because the formula feels logical. You're testing a row-level fact (this order's region) and returning a summary (total sales). In Excel that pattern works fine. In Tableau it can't, because Tableau builds queries at a specific level of detail and every part of one calculation must agree on that level.

The stakes are real. A broken calculated field turns every sheet that uses it red, and if that field feeds a published dashboard, your Monday audience sees the failure. Worse, the quick fixes people reach for — wrapping everything in ATTR() until the error disappears — can silently return wrong numbers that look right.

By the end of this article you'll read the error as plain English: two granularities collided. You'll know the three legitimate repairs (restructure the IF, wrap the dimension, or write a LOD), you'll understand exactly what ATTR() promises, and you'll have a checklist that keeps this error off dashboards for good.

What Tableau Means by Mixing Aggregate and Non-Aggregate Arguments

Every Tableau calculation executes at one level of detail, and every function in that expression must agree on it. SUM([Sales]) collapses many rows into one number — that's aggregate. Bare [Region] returns one value per row — that's non-aggregate. Combine them in IF [Region] = "West" THEN SUM([Sales]) END and Tableau faces an impossible question: should it evaluate per row or per view? It refuses to guess, and the error is that refusal.

Think of pills on the shelf as a contract. Dimensions slice the view into partitions; measures aggregate within each partition. A calculated field inherits this contract. When your formula asks for a row's region and the partition's total in the same breath, no single SQL GROUP BY can satisfy both, so compilation fails before any data is touched.

Beginners trip here because each half works alone. [Region] = "West" is a valid boolean. SUM([Sales]) is a valid number. The error only appears at combination time, which feels like Tableau is being stubborn. It's actually protecting you: a silent guess about granularity would produce numbers that look right and aren't.

The mental model that ends the confusion is simple. Label every piece of a new calc as row or aggregate before you write it. If both labels appear, stop — you need one of the three repairs in this article. Once labeling becomes habit, you'll catch the error in your head before Tableau catches it on screen.

This matters beyond one error dialog. Granularity discipline is the same skill behind correct LOD expressions, trustworthy table calcs, and totals that reconcile. Master it here and every advanced Tableau topic gets easier.

TABLEAU
1
2
3
4
5
6
7
8
9
// BROKEN: row-level test + aggregated result in one expression
IF [Region] = "West" THEN SUM([Sales]) END
// Error: cannot mix aggregate and non-aggregate arguments

// FIXED: classify each row first, then aggregate
SUM(IF [Region] = "West" THEN [Sales] END)

// FIXED with ELSE branch kept explicit
SUM(IF [Region] = "West" THEN [Sales] ELSE 0 END)
📊 Production Insight
Published dashboards fail hardest on this error because one broken field reds out every sheet that references it. Keep a scratch sheet per workbook where new calcs prove themselves before they touch shared dashboards.
🎯 Key Takeaway
The error means two granularities collided in one expression. Label each piece row or aggregate, then restructure so all pieces agree.

The IF Trap: Row-Level Tests Wrapped Around SUM Totals

The IF function is where most analysts meet this error, because IF invites you to test a dimension and return a measure. IF [Region] = "West" THEN SUM([Sales]) END reads like English, which is exactly why it's wrong: the condition lives at row level while the result lives at view level, and Tableau cannot evaluate both levels in one pass.

The repair reverses the nesting. SUM(IF [Region] = "West" THEN [Sales] END) tests every row first — each order is West or it isn't — and then sums the survivors. The IF now operates purely at row level, and SUM aggregates the outcome. Same business logic, one consistent granularity, no error.

Date comparisons follow the same rule. IF [Order Date] >= #2024-01-01# THEN SUM([Sales]) END fails identically, and the fix is SUM(IF [Order Date] >= #2024-01-01# THEN [Sales] END). Any time your condition references a raw field and your result aggregates, push the aggregation outside.

ELSE branches deserve attention too. Omitting ELSE leaves NULL for non-matching rows, and SUM ignores NULLs — usually what you want. Writing ELSE 0 instead includes zeros, which changes AVG but not SUM. Pick deliberately: for averages over matched rows only, leave ELSE out; for averages over all rows, write ELSE 0.

Nested IFs multiply the risk. Each branch must independently respect the granularity contract. When nesting gets deep, split the row-level classification into its own boolean field — [Is West] as [Region] = "West" — then aggregate cleanly with SUM(IF [Is West] THEN [Sales] END). Two simple fields beat one clever one.

TABLEAU
1
2
3
4
5
6
7
8
9
10
// BROKEN: date test at row level, result aggregated
IF [Order Date] >= #2024-01-01# THEN SUM([Sales]) END

// FIXED: test inside, aggregate outside
SUM(IF [Order Date] >= #2024-01-01# THEN [Sales] END)

// CLEANEST: split classification from aggregation
// [Is West]  :=  [Region] = "West"
SUM(IF [Is West] THEN [Sales] END)
AVG(IF [Is West] THEN [Profit] END)  // NULLs excluded from average
📊 Production Insight
Splitting classification booleans from aggregations makes workbooks auditable: reviewers can validate [Is West] row counts once and then trust every SUM built on it.
🎯 Key Takeaway
Classify first, aggregate second. SUM(IF <row test> THEN [Measure] END) is the default safe shape for conditional totals.

ATTR() Under the Microscope: Fix, Workaround, or Silent Lie

ATTR() is Tableau's special aggregation: if all rows in a partition share one value, it returns that value; if they differ, it returns '*'. That contract makes IF ATTR([Region]) = "West" THEN SUM([Sales]) END compile — both sides are now aggregate. But compiling and being correct are different things, and ATTR() punishes blind trust.

ATTR() tells the truth at detail levels where one value genuinely exists per partition. A view sliced by Order ID with ATTR([Region]) is safe: each order has exactly one region. The same calc at a yearly total mixes regions, returns '*', and the IF comparison against "West" fails — the row quietly vanishes from your result instead of erroring loudly.

That silent drop is the production hazard. The incident in this article lost 18% of bonus sales for a week because '*' rows excluded themselves without any warning. The error dialog is annoying but honest; an ATTR() patch that compiles can be wrong and quiet, which is far more expensive.

Use ATTR() as a diagnostic, not a default. When it returns '*', treat that as information: your partition genuinely holds multiple values, and you must decide what the business wants — filter to one, sum conditionally, or compute per-value with a LOD. The asterisk is Tableau asking you a question. Answer it instead of hiding it.

Reserve ATTR() for display-only labels you have verified single-valued, like showing a customer's segment next to their LOD total. For anything that feeds money math, prefer MIN()/MAX() with a documented guarantee or a conditional SUM that can't produce '*'.

TABLEAU
1
2
3
4
5
6
7
8
9
10
// COMPILES but dangerous at mixed totals
IF ATTR([Region]) = "West" THEN SUM([Sales]) END
// At a total mixing regions, ATTR() = "*" and the row drops out

// SAFE: conditional aggregation never produces "*"
SUM(IF [Region] = "West" THEN [Sales] END)

// LEGITIMATE ATTR() use: single value guaranteed per partition
// View sliced by [Order ID], one region per order
ATTR([Region])
📊 Production Insight
Any ATTR() that feeds money math needs a totals-level spot check before publishing: compare the ATTR-based total against a conditional SUM on the same view and investigate any gap.
🎯 Key Takeaway
ATTR() returns '*' when values mix, silently dropping rows. Use it for verified single-value labels, never as a blind patch for money calculations.

MIN() and MAX() Wrappers: The Safer Way to Satisfy the Compiler

When a dimension genuinely holds one value per partition, MIN() and MAX() convert it to an aggregate without ATTR()'s asterisk behavior. IF MIN([Region]) = "West" THEN SUM([Sales]) END compiles, and at mixed totals it returns an actual region instead of ''. That determinism makes failures visible rather than silent — a wrong-but-visible value gets caught, while '' rows just disappear.

Dates benefit most from this pattern. IF MIN([Order Date]) >= #2024-01-01# THEN SUM([Sales]) END tests the earliest date in each partition, which is meaningful for cohort logic like 'customers whose first order was this year.' ATTR([Order Date]) in the same spot would star out on any partition with two dates, killing the comparison.

The wrapper must be semantically honest. MIN([Region]) on a partition with both West and East returns East alphabetically — deterministic but arbitrary. If your partitions can mix, the wrapper compiles yet the business logic is undefined. Only wrap when the view's granularity guarantees single values, such as Order ID determining Region functionally.

A useful test: add COUNTD([Field]) beside your calc. If it ever exceeds 1 in a partition your calc touches, your MIN()/MAX() wrapper is hiding variation that the business should decide about. Either filter, split the view, or switch to conditional aggregation.

Compared with ATTR(), MIN()/MAX() fail loudly and sort predictably, which is why experienced developers prefer them. Neither replaces restructuring the calc when restructuring is possible — but when a wrapper is truly warranted, MIN() or MAX() with a COUNTD guard is the professional choice.

TABLEAU
1
2
3
4
5
6
7
8
9
10
// Deterministic wrapper: earliest date per partition
IF MIN([Order Date]) >= #2024-01-01# THEN SUM([Sales]) END

// Guard query: COUNTD above 1 means the wrapper hides variation
COUNTD([Region])

// Same pattern in raw SQL: bare column must be grouped or aggregated
SELECT Region, SUM(Sales) AS TotalSales
FROM Orders
GROUP BY Region;
📊 Production Insight
Pair every MIN()/MAX() dimension wrapper with a COUNTD check during development; the one time COUNTD exceeds 1 is the time the wrapper would have lied in production.
🎯 Key Takeaway
MIN() and MAX() beat ATTR() for single-value wrappers because they fail visibly instead of starring out — but only wrap when one value per partition is guaranteed.

The LOD Escape Hatch: FIXED Calculations That End the Fight

Sometimes you genuinely need two levels in one view: each row's sales beside its customer's total. No single-granularity calc can do that — which is exactly what Level of Detail expressions solve. { FIXED [Customer ID] : SUM([Sales]) } computes the total at customer level regardless of view layout, then lets each row compare itself against it.

The classic ratio calc shows the power: SUM([Sales]) / SUM({ FIXED [Customer ID] : SUM([Sales]) }) gives each row's share of its customer total. The numerator follows the view; the denominator is pinned. Both are aggregate, so they combine cleanly, yet they embody different levels. That's the escape hatch the raw error denies you.

FIXED ignores dimension filters unless they're added to context, which is the sharp edge. A regional filter won't shrink a FIXED total by default — your numerator filters but your denominator doesn't, and shares exceed 100%. Right-click the filter, choose Add to Context, and the LOD respects it. Test every LOD calc with its filters on and off before publishing.

INCLUDE and EXCLUDE tune rather than pin. { INCLUDE [Segment] : SUM([Sales]) } adds granularity finer than the view; { EXCLUDE [Region] : SUM([Sales]) } computes coarser than the view. Reach for them when FIXED feels too rigid, but default to FIXED for totals that must survive any layout change.

LOD expressions cost queries, so use them where the business question demands mixed granularity — not as a reflex for every mismatch error. Restructure first, wrap second, LOD third. When you do need LOD, it's the correct tool, not a workaround.

TABLEAU
1
2
3
4
5
6
7
8
// Per-customer total independent of view layout
{ FIXED [Customer ID] : SUM([Sales]) }

// Each row's share of its customer total
SUM([Sales]) / SUM({ FIXED [Customer ID] : SUM([Sales]) })

// Grand total pinned for percent-of-total views
{ FIXED : SUM([Sales]) }
⚠ FIXED LODs Ignore Dimension Filters by Default
A FIXED total won't shrink when you filter Region unless that filter is added to context. Right-click the filter and choose Add to Context, then verify the denominator moves with the filter before you trust any share-of-total math.
📊 Production Insight
LOD totals that ignore filters are the number-one source of shares exceeding 100% on executive dashboards. A two-minute context-filter check saves an embarrassing leadership meeting.
🎯 Key Takeaway
Use FIXED LOD when the question truly needs two levels, and always verify dimension filters are in context so numerator and denominator agree.

A Prevention Checklist So This Error Never Reaches Your Dashboard

Prevention beats repair, and this error is highly preventable with three habits. First, label granularity before writing: mark each field row or aggregate in a comment line above the calc. If both labels appear, you already know a restructure, wrapper, or LOD is required — no surprise dialog needed.

Second, build conditional logic in two fields. A boolean classification field ([Is West], [Is Recent]) holds the row-level test and can be unit-checked with row counts. The aggregation field (SUM(IF [Is West] THEN [Sales] END)) holds the math. Reviewers validate each half independently, and future editors can't accidentally reintroduce the mix.

Third, maintain a scratch validation sheet in every workbook. It shows COUNTD guards beside wrapped dimensions, conditional SUMs beside ATTR() versions, and LOD totals beside table-calc replications. When all three agree, publish. When they diverge, you've caught the next incident in development instead of in front of leadership.

Extend the discipline to totals and subtotals. Every new calc gets a totals glance: does the total equal the sum of visible rows? ATTR()-based calcs fail this test by design at mixed levels, which is your cue to replace them with conditional aggregation before anyone relies on the number.

Finally, document the exceptions. When a MIN() wrapper or FIXED LOD is genuinely correct, say why in a comment: which functional dependency guarantees single values, which filters sit in context. The next analyst inherits your reasoning, not just your formula — and that's what keeps dashboards correct after you've moved on.

📊 Production Insight
Teams that require a validation sheet per workbook catch granularity bugs in development; teams that don't catch them in executive reviews, where every fix costs credibility.
🎯 Key Takeaway
Label granularity first, split classification from aggregation, and validate totals before publishing — three habits that retire this error permanently.
● Production incidentPOST-MORTEMseverity: high

The Monday Sales Dashboard That Turned Red Over One IF Statement

Symptom
Five dashboard sheets showed the red 'cannot mix aggregate and non-aggregate arguments' error at 8:40 AM, twenty minutes before a leadership review. The underlying data source was healthy and other dashboards using the same extract rendered normally. Only sheets referencing the new [West Bonus Sales] field failed, which pointed at the calculation rather than the data or the server.
Assumption
The analyst assumed that because [Region] = "West" worked in a filter and SUM([Sales]) worked in a measure, combining them with IF was safe. Excel habits reinforced this: in a spreadsheet row you can freely mix a cell test with a SUM range. Nobody suspected granularity, so the first hour went to re-extracting data and republishing the workbook instead of reading the formula.
Root cause
The field was defined as IF [Region] = "West" THEN SUM([Sales]) END. [Region] is row-level (non-aggregate) while SUM([Sales]) is aggregated, so Tableau could not compile the expression at any single level of detail. The first patch wrapped the dimension — IF ATTR([Region]) = "West" THEN SUM([Sales]) END — which compiled but returned '*' at totals where multiple regions mixed, understating West bonus sales by 18% for a week before anyone noticed.
Fix
The team replaced the field with SUM(IF [Region] = "West" THEN [Sales] END), pushing the row test inside the aggregation so every row is classified first and summed second. They added a FIXED LOD version for the customer-level view: { FIXED [Customer ID] : SUM(IF [Region] = "West" THEN [Sales] END) }. They also pinned a validation sheet showing row counts per region next to the dashboard, so a future granularity slip shows up as a visible mismatch instead of a silent wrong total.
Key lesson
  • Read the error literally: it always names a granularity collision, so look for the one field in your calc that sits at a different level from the rest instead of rebuilding extracts or republishing workbooks.
  • ATTR() that compiles is not ATTR() that is correct — at mixed totals it returns '*' and drops the row, so every ATTR() patch needs a totals-level spot check before it ships to leadership.
  • Classify first, aggregate second: SUM(IF <row test> THEN [Measure] END) is the default safe shape, and it should be your muscle memory before you reach for LOD expressions.
Production debug guideFive checks that take you from red error to correct numbers, in the order a Tableau specialist actually runs them.5 entries
Symptom · 01
Calculated field shows 'cannot mix aggregate and non-aggregate arguments' and every sheet using it is red
→
Fix
Open the calc editor and label each function: mark SUM, AVG, COUNTD, MIN, MAX, ATTR as aggregate and everything else as row-level. The error fires when both labels appear in one expression. Rewrite IF [Dim] = X THEN SUM([M]) END as SUM(IF [Dim] = X THEN [M] END). Validate by clicking Apply — the error clears immediately — then drop the field on a test view sliced by [Dim] and confirm each slice matches a manual filter total.
Symptom · 02
Error appears only after you drag a new dimension onto Rows or Columns
→
Fix
The view's level of detail changed under a previously valid calc. A field that returned one value per pane now returns many, which breaks ATTR() or a bare dimension inside an aggregate. Check the view granularity: right-click the pill layout and note the dimensions present. Fix by wrapping the dimension with MIN() or MAX() if one value per partition is guaranteed, or rewrite with INCLUDE/EXCLUDE LOD to pin the calc's level independent of the view. Confirm by adding and removing the dimension while watching the value stay stable.
Symptom · 03
ATTR([Field]) compiles but shows '*' in totals or subtotals
→
Fix
The asterisk is ATTR() telling you multiple values collided at that level — Tableau returns '*' instead of guessing. Drill into the total: right-click it and choose View Data to see which distinct values mixed. Decide what the total should mean: if you want the West slice only, filter or use SUM(IF [Region] = "West" THEN [Sales] END); if you want per-region detail preserved, keep ATTR() in body rows but replace the total with a separate LOD total. Confirm the total now equals the sum of visible body rows.
Symptom · 04
You need row-level detail and a view total in the same calculation
→
Fix
Stop fighting one granularity — compute the total at its own level with a FIXED LOD. Write { FIXED [Customer ID] : SUM([Sales]) } to get per-customer totals that survive any view layout, then compare each row with [Sales] / { FIXED [Customer ID] : SUM([Sales]) }. Validate against a table calc WINDOW_SUM alternative on a small sample: both should agree to the cent. If the LOD total ignores a dimension filter you need, add it to context before trusting the number.
Symptom · 05
Error survives every rewrite and mentions a table calculation or secondary field
→
Fix
A nested dependency is smuggling the wrong granularity in: a referenced calculated field or a blended secondary field brings its own level. Right-click the failing calc, choose Describe, and expand every referenced field to find the hidden aggregate or row-level piece. Isolate by creating a scratch calc with only the suspect reference and testing it alone. Fix the dependency at its source — don't wrap the outer calc — then recompile the outer expression and confirm all downstream sheets clear.
Aggregate Mismatch Causes Compared
Root CauseHow to ConfirmFixPrevention
IF tests a row field but returns SUMCalc matches IF [Dim] = X THEN SUM([M]) ENDPush SUM outside: SUM(IF [Dim] = X THEN [M] END)Write classification booleans separately from aggregations
Bare dimension beside an aggregateOne pill is green (measure) and the other is blue (dimension)Wrap with MIN()/MAX() only if single value is guaranteedLabel every field row or aggregate before writing the calc
ATTR() starring out at mixed totalsTotal shows '*' or row vanishes while body rows look fineReplace with conditional SUM or a LOD total for that levelSpot-check every ATTR() calc at the totals level before publishing
Row detail and view total in one viewRequirement says 'each row vs its group total' explicitlyFIXED LOD for the total, then ratio against row valuesAdd dimension filters to context and reconcile LOD vs table calc
⚙ Quick Reference
5 commands from this guide
FileCommand / CodePurpose
IF [Region] = "West" THEN SUM([Sales]) ENDWhat Tableau Means by Mixing Aggregate and Non-Aggregate Arg
IF [Order Date] >= #2024-01-01# THEN SUM([Sales]) ENDThe IF Trap
IF ATTR([Region]) = "West" THEN SUM([Sales]) ENDATTR() Under the Microscope
IF MIN([Order Date]) >= #2024-01-01# THEN SUM([Sales]) ENDMIN() and MAX() Wrappers
{ FIXED [Customer ID] : SUM([Sales]) }The LOD Escape Hatch

Key takeaways

1
The error always means two granularities collided in one expression
label each piece row or aggregate to spot it.
2
SUM(IF <row test> THEN [Measure] END) is the default safe shape
classify rows first, aggregate second.
3
ATTR() returns '*' on mixed partitions and silently drops rows
never let it feed money math unchecked.
4
MIN()/MAX() wrappers fail visibly instead of starring out, but need a COUNTD guard proving single values.
5
FIXED LOD solves true two-level questions, yet ignores dimension filters unless they're added to context.
6
Split classification booleans from aggregations and validate totals on a scratch sheet before publishing.

Common mistakes to avoid

5 patterns
×

Wrapping everything in ATTR() until the error disappears

Symptom
Calc compiles but totals show '*' or rows silently drop, understating figures with no warning.
Fix
Replace ATTR() patches on money math with SUM(IF <test> THEN [Measure] END) and reserve ATTR() for verified single-value labels.
×

Testing a dimension outside SUM instead of inside it

Symptom
IF [Region] = "West" THEN SUM([Sales]) END fails the moment both halves combine, though each half works alone.
Fix
Invert the nesting to SUM(IF [Region] = "West" THEN [Sales] END) so classification happens per row and aggregation happens last.
×

Trusting FIXED LOD totals alongside dimension filters

Symptom
Shares of total exceed 100% because the numerator filters by region while the FIXED denominator ignores the filter.
Fix
Right-click each needed filter, choose Add to Context, and verify the LOD denominator moves when the filter changes.
×

Using ELSE 0 without thinking about averages

Symptom
AVG over conditionally matched rows drops because ELSE 0 rows dilute the average that should exclude non-matches.
Fix
Omit ELSE (leaving NULL) when non-matches should be excluded from AVG; write ELSE 0 only when zeros are genuine data points.
×

Nesting three IFs deep instead of splitting fields

Symptom
A mega-calc mixes levels in one unreadable branch and nobody can spot which branch breaks granularity.
Fix
Split row tests into boolean fields first, validate their counts, then aggregate — two simple fields beat one clever expression.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01JUNIOR
Why does IF [Region] = "West" THEN SUM([Sales]) END throw the aggregate ...
Q02JUNIOR
What does ATTR() actually return, and when is it unsafe?
Q03SENIOR
When would you choose MIN() over ATTR() as a dimension wrapper?
Q04SENIOR
How does a FIXED LOD resolve a genuine two-level requirement, and what's...
Q05SENIOR
A dashboard total doesn't equal the sum of its visible rows. How do you ...
Q01 of 05JUNIOR

Why does IF [Region] = "West" THEN SUM([Sales]) END throw the aggregate mismatch error?

ANSWER
Because [Region] is row-level and SUM([Sales]) is aggregated, so the expression demands two levels of detail at once. Tableau can't compile that to a single GROUP BY. The fix is SUM(IF [Region] = "West" THEN [Sales] END), which classifies rows first and aggregates second at one consistent level.
FAQ · 6 QUESTIONS

Frequently Asked Questions

01
Why does my calc work until I add a dimension to the view?
02
Is ATTR() ever the right fix?
03
What's the difference between SUM(IF X THEN [Sales] END) and SUM(IF X THEN [Sales] ELSE 0 END)?
04
Can I mix a table calculation with a regular aggregate?
05
Why does my FIXED LOD total ignore my filter?
06
How do I explain this error to a non-technical stakeholder?
N
Naren Founder & Principal Engineer

20+ years shipping production backend systems. Notes here come from systems that actually shipped.

Follow
✓ Verified
production tested
September 27, 2026
last updated
2,085
articles · all by Naren
🔥

That's Tableau. Mark it forged?

7 min read · try the examples if you haven't

←
Previous
Power BI Measure vs Calculated Column: When Each Breaks
1 / 5 · Tableau
Next
Tableau Cannot Blend Secondary Data Source
→