// Tableau + AI · Calculations · LOD · Prompts

Write Tableau Calculated Fields and LODs with AI

Prompts that give the model your schema and grain, worked FIXED, INCLUDE, EXCLUDE, table calc, date and null examples in valid Tableau syntax, the nine mistakes LLMs make in Tableau calcs, and a checklist to verify them.

OCT 14, 2026

I use AI to draft Tableau calculations almost every day. It is genuinely good at it, and it is also wrong in very specific, very repeatable ways. The model has read a mountain of SQL, Excel and Python, and much less Tableau, so when it's unsure it quietly borrows syntax and assumptions from those languages. The calc looks right, it might even validate, and the number is still wrong.

This guide is how I get good calcs out of Claude or ChatGPT: the prompt shape that works, worked examples in real Tableau syntax, the mistakes I see over and over, and the checklist I run before any AI-written calc goes near a published workbook.

Why does AI get Tableau calculations wrong?

Because a Tableau calculation isn't just a formula. Its answer depends on three things the model can't see unless you tell it:

  1. The grain of the data. Is one row an order, an order line, a daily snapshot, a customer? SUM([Sales]) means different things at each grain.
  2. The level of detail of the view. What's on Rows, Columns and the Marks card decides how aggregates and table calcs resolve.
  3. Where filters run. Tableau applies filters in a fixed order of operations. A FIXED LOD and a table calculation sit at very different points in it.

A chatbot sees none of that. It sees your sentence. So the single biggest improvement you can make is in the prompt, before the model writes a character.

How do I prompt AI to write a Tableau calculated field?

Give it the schema, the grain, the view and the definition in words. Then ask for things that force it to reason about level of detail instead of just pattern-matching. This is the template I use:

TEXT
You are writing a Tableau calculated field. Use Tableau calculation syntax
only: no SQL, no Excel, no Python.

Data source: Orders (published extract)
Grain: one row per order line. An order can have many lines.
Fields:
  [Order ID]       string, dimension
  [Order Date]     date, dimension
  [Ship Date]      date, dimension
  [Customer ID]    string, dimension
  [Region]         string, dimension
  [Category]       string, dimension
  [Sales]          float, measure
  [Profit]         float, measure
  [Discount]       float, measure, can be null

View: [Region] on Rows, YEAR([Order Date]) on Columns.
Filters: [Category] as a regular dimension filter.

I want: each customer's first order date, so I can count new customers per year.

Give me:
1. The calculation.
2. The level of detail it computes at, and whether it is an LOD or table calc.
3. Which filters it respects and which it ignores.
4. Three small test cases I can check by hand in a crosstab.

Points 2 and 3 do most of the work. When the model has to say out loud "this FIXED expression ignores your Category filter," it either catches its own mistake or hands you the exact thing you need to check.

Worked examples: LOD expressions with AI

Here are prompts I've actually given, with the calc I'd accept. Every one of these is valid Tableau syntax. The Tableau help page on Level of Detail expressions is the reference for all three keywords.

FIXED: first purchase date per customer

TEXT
{ FIXED [Customer ID] : MIN([Order Date]) }

Name it First Order Date. Because it's FIXED, it returns one value per customer regardless of what's in the view, and you can use it at row level. To flag new versus returning orders:

TEXT
IF [Order Date] = [First Order Date] THEN "New" ELSE "Returning" END

And a repeat-customer flag:

TEXT
{ FIXED [Customer ID] : COUNTD([Order ID]) } > 1

EXCLUDE: share of region total

With [Region] and [Category] in the view, this gives each category's share of its region:

TEXT
SUM([Sales]) / SUM({ EXCLUDE [Category] : SUM([Sales]) })

The outer SUM is there because the LOD result has to be aggregated to sit beside SUM([Sales]) in the same expression.

INCLUDE: average order size

The data is at order-line grain, so AVG([Sales]) gives the average line, not the average order. INCLUDE computes per order first, then averages:

TEXT
AVG({ INCLUDE [Order ID] : SUM([Sales]) })

This is a great test of a model. If you ask for "average order value" without stating the grain, most models write AVG([Sales]). State the grain and they get it right.

Worked examples: table calcs, dates and nulls

Running total and rank

TEXT
RUNNING_SUM(SUM([Sales]))
TEXT
RANK(SUM([Sales]))

Both are table calculations. The formula is half the answer; the other half is Compute Using (Table Across, Pane Down, specific dimensions). Always ask the model to say which addressing and partitioning it assumes. It can't set it for you, and the default is often not what you meant.

Year over year growth

TEXT
(SUM([Sales]) - LOOKUP(SUM([Sales]), -1)) / ABS(LOOKUP(SUM([Sales]), -1))

Computed along YEAR([Order Date]). The first year returns null because there's nothing to look back to, which is correct, and worth telling stakeholders about rather than hiding.

Date math and date parsing

Days to ship:

TEXT
DATEDIFF('day', [Order Date], [Ship Date])

Month to date:

TEXT
[Order Date] >= DATETRUNC('month', TODAY()) AND [Order Date] <= TODAY()

Parsing a string date:

TEXT
DATEPARSE("yyyy-MM-dd", [Order Date Text])

Note the argument order: the format comes first, then the string. The date functions reference also points out that DATE() recognizes many standard formats on its own, so try DATE([Order Date Text]) before you reach for a pattern.

Null handling

TEXT
ZN(SUM([Sales]))
TEXT
IFNULL([Region], "Unassigned")

ZN returns the expression, or zero if it's null, so it's for numbers only. IFNULL(expr1, expr2) works for any type. ISNULL returns a boolean. Tableau's logical functions page covers all three.

What mistakes do LLMs make in Tableau calculations?

These are the ones I catch most often. Paste this list into your prompt as "avoid these" and the hit rate goes up noticeably.

1. LOD versus table calc confusion. Ask for "percent of total" and you might get an EXCLUDE LOD when you wanted SUM([Sales]) / TOTAL(SUM([Sales])), or the reverse. They behave differently: the table calc respects every filter and the view's partitioning; the LOD is computed in the data source. Decide which one you want and say so.

2. Ignoring filter order of operations. A FIXED LOD is computed before dimension filters. So if you filter to one Category, { FIXED [Customer ID] : MIN([Order Date]) } still sees every category. Sometimes that's right (true first purchase, ever). If you want it to respect the filter, add the filter to context. INCLUDE and EXCLUDE run after dimension filters. Table calcs run after nearly everything, which is why a table-calc filter can hide rows without changing a running total.

3. Mixing aggregate and non-aggregate. This is the classic "Cannot mix aggregate and non-aggregate arguments with this function" error:

TEXT
IF [Region] = "West" THEN SUM([Sales]) END

Two valid fixes, depending on what you mean:

TEXT
SUM(IF [Region] = "West" THEN [Sales] END)
TEXT
IF ATTR([Region]) = "West" THEN SUM([Sales]) END

4. Misreading ATTR. Models sprinkle ATTR() around to silence the aggregate error. ATTR returns the value only when every row in the partition has the same one; otherwise it returns *. If your mark spans more than one region, the second fix above returns null, not an error. That can be correct, but know it's happening.

5. Ratio of sums versus sum of ratios. A row-level [Profit] / [Sales] dropped on the view gets summed: you get the sum of row ratios, which is meaningless. Profit ratio is SUM([Profit]) / SUM([Sales]).

6. INT and division habits from SQL. In Tableau, / is decimal division. INT() truncates toward zero, it doesn't round: the type conversion docs give INT(8/3) = 2 and INT(-9.7) = -9. Use ROUND() when you mean rounding, and DIV(11, 2) when you really want integer division. Watch for SQL leftovers like CAST(x AS FLOAT); Tableau's casts are FLOAT(), INT(), STR(), DATE().

7. String date parsing. Models swap the DATEPARSE arguments, or use Python codes like %Y-%m-%d. Tableau wants a pattern like "yyyy-MM-dd", and MM (month) is not mm (minutes).

8. ZN versus IFNULL, and nulls inside averages. ZN on a string field fails; use IFNULL. Subtler: AVG(ZN([Discount])) and AVG([Discount]) are different numbers. The first counts null discounts as zero; the second skips them. Neither is wrong. Only one matches your metric definition.

9. SQL-shaped CASE and ELSE IF. Tableau's CASE compares one expression to values (CASE [Region] WHEN "West" THEN 1 ELSE 0 END). For conditions, use IF ... ELSEIF ... END. Note ELSEIF is one word. A model that writes ELSE IF has started a nested IF that needs its own END.

How do I check an AI-written Tableau calc?

I don't trust a calc because it validated. "The calculation is valid" only means it parses. This is my checklist:

  • Build a crosstab next to it. Put the dimensions you care about on Rows, the raw measures and the new calc side by side, and check three rows by hand. If the model gave you test cases, use those.
  • Check the grand totals. Turn on Analysis, Totals. LODs and table calcs often look right per row and wrong in the total, or the total is computed differently from what a stakeholder expects.
  • Flip the filters. Apply each filter in the view and watch whether the calc moves when it should and stays put when it should.
  • Test the nulls. Filter to rows where the inputs are null. Does the calc return null, zero, or something odd?
  • Change the level of detail. Add or remove a dimension. A FIXED LOD shouldn't change; an INCLUDE or table calc should.
  • Read the explanation back. Ask the model to explain the calc in plain English, then compare that to your metric definition. If the explanation doesn't match the definition, the calc doesn't either.
  • Name and comment it. Tableau calcs accept // comments. Have the AI write a one-line comment with the grain and the filter behavior. Your future self will thank you.

Should I use Tableau Agent or a chatbot for calcs?

If you have Tableau Agent, it has a real advantage: it can see your fields. A chatbot only knows what you paste. I compare the options job by job in the best AI tools for Tableau developers. Whatever writes the calc, the checklist above still applies, because none of them can see your stakeholder's definition of "active customer."

For the bigger picture of what to hand to AI and what to keep, read AI for Tableau developers. If you want a second opinion on a whole workbook rather than one calc, see how to review a Tableau workbook with AI.

Next step

Take the prompt template above, swap in your own schema and grain, and ask for one calc you already know the right answer to. Check it with the crosstab. Once you see where the model slips on your data, you'll know exactly what to put in the prompt next time. More guides like this live on the Tableau + AI hub.

Frequently asked questions

Can ChatGPT or Claude write Tableau LOD expressions?
Yes, and well, if you give them the grain of the data, the fields in the view and the filters. Without that context they often confuse LODs with table calculations or ignore Tableau's filter order of operations, so always check the result against a crosstab.
Why does my AI-written FIXED LOD ignore my filter?
FIXED LOD expressions are computed before dimension filters in Tableau's order of operations. To make a FIXED expression respect a filter, add that filter to context. INCLUDE and EXCLUDE expressions are computed after dimension filters.
What is the difference between ZN and IFNULL in Tableau?
ZN(expression) returns the expression, or zero if it is null, so it only works for numbers. IFNULL(expr1, expr2) returns expr1 if it is not null and expr2 otherwise, and works for any data type. ISNULL returns true or false.
How do I fix 'Cannot mix aggregate and non-aggregate arguments'?
Either move the condition inside the aggregation, as in SUM(IF [Region] = "West" THEN [Sales] END), or aggregate the dimension with ATTR, as in IF ATTR([Region]) = "West" THEN SUM([Sales]) END. They behave differently when a mark spans more than one value.
How do I check a Tableau calculation written by AI?
Put it in a crosstab beside the raw measures and check a few rows by hand, turn on grand totals, apply each filter, test rows with null inputs, add or remove a dimension, and ask the model to explain the calc in plain English and compare that to your metric definition.