Skip to content

Translate Tableau calculations to DuckDB SQL.

Keep the calculation grain explicit when rewriting workbook formulas as SQL. This sample turns customer-level sales into one grouped SQL result per customer.

Updated

Read locally in this browser.

↳sample.csvSynthetic sample

What is total sales at customer grain?

A synthetic export has two records for customer A, two for B and one for C. SQL returns A=150, B=100 and C=200 at customer grain.

“Rewrite the customer-level FIXED total as grouped SQL.”

Worked example · authored synthetic data

  1. Read the original fileUse the existing browser reader
  2. Check the definitionKeep the grouping and filter scope explicit
  3. Compute the resultUse the existing xmetric engine
  4. Review the evidenceRead the exact cells and query

Calculation or query in this example

SELECT Customer, SUM(Sales) AS Sales FROM input GROUP BY Customer ORDER BY Customer

Exact sample file · Definitions and expected values

xmetricSales by Customer

Opening the labelled sample…

Expected values from the labelled sample; the browser recomputes them.

GroupResult
A150
B100
C200
Use your own file

Drop one file anywhere on this page. It is read in your browser; no file is uploaded.

Nothing is uploaded.

Read locallyThe original file stays in the browser.

Share deliberatelyMCP shares supported metadata and requested results with the chat host.

Separate actionsOnline saves and support uploads require their own actions.

How does this workflow work?

What is the direct answer to this workbook question?

To migrate a Tableau workbook to DuckDB SQL, map row-level calculated fields to SELECT expressions, convert FIXED, INCLUDE, and EXCLUDE Level of Detail expressions into window functions or CTE subqueries, and translate table calculations into OVER() clauses. You can drop a .twbx file into the xmetric inspector to list its formulas before rewriting.

Why migrate workbook calculations to DuckDB SQL?

Many analysts store business logic inside .twbx workbooks tied to a desktop GUI—Tableau Desktop offers a free local edition for one person (without publishing or sharing) and paid Creator licenses at $75 per user/month, billed annually (as of October 2026). Rewriting those calculations into DuckDB SQL (a free, MIT-licensed in-process analytical SQL database) lets coding agents, Python scripts, CI pipelines, and browser BI tools run the exact same metrics over Parquet and CSV files directly in code.

Drop your .twbx or .twb into the xmetric workbook inspector first to list every data source, worksheet, and calculated field (and spot fields nothing uses or formulas that refer to a missing field) in your browser so you know which formulas are actually used before translating them.

How do common Tableau calculations translate to SQL?

Use scalar SQL for row expressions, explicit grouping or subqueries for LOD grain, and ordered SQL windows for supported table calculations.

Tableau Function / SyntaxDuckDB SQL EquivalentKey Difference to Watch
ZN([Profit])COALESCE(profit, 0)ZN only applies to numeric fields; COALESCE works on any type.
IFNULL([Region], 'Unknown')COALESCE(region, 'Unknown')Direct 1:1 replacement.
IIF([Sales] > 100, 'High', 'Low')CASE WHEN sales > 100 THEN 'High' ELSE 'Low' ENDTableau IIF accepts an optional 4th argument for UNKNOWN/NULL.
DATETRUNC('month', [Order Date])DATE_TRUNC('month', order_date)Identical date part strings for day, week, month, quarter, year.
DATEDIFF('day', [Start], [End])DATE_DIFF('day', start_date, end_date)Note the underscore in DuckDB's DATE_DIFF.
DATEADD('day', 7, [Order Date])order_date + INTERVAL 7 DAYDuckDB uses SQL INTERVAL arithmetic (or DATE_ADD).
COUNTD([Customer ID])COUNT(DISTINCT customer_id)Exact distinct count in DuckDB (approx_count_distinct available for speed).
CONTAINS([Name], 'Pro')CONTAINS(name, 'Pro')Both are case-sensitive by default; use ILIKE '%Pro%' for case-insensitive matching.

How can I translate LOD grain into DuckDB SQL?

Level of Detail expressions are the hardest part of a Tableau-to-SQL migration because an LOD computes an aggregation at a grain different from the worksheet's GROUP BY shelves.

How can a FIXED LOD map to a CTE or window function?

When a worksheet groups by Region and Order Month, but a calculation needs each customer's first purchase date across all history:

// Tableau FIXED LOD
{ FIXED [Customer ID] : MIN([Order Date]) }
-- DuckDB SQL using a window function before the final aggregation
WITH customer_cohorts AS (
  SELECT
    customer_id,
    region,
    order_date,
    sales,
    MIN(order_date) OVER (PARTITION BY customer_id) AS first_order_date
  FROM orders
)
SELECT
  DATE_TRUNC('month', first_order_date) AS cohort_month,
  region,
  SUM(sales) AS total_sales
FROM customer_cohorts
GROUP BY 1, 2;

How can an EXCLUDE LOD map to a windowed denominator?

An EXCLUDE expression removes a dimension that is currently on the view shelves, usually to compute a percent of parent total:

// Tableau EXCLUDE LOD (on a view grouped by Region and Category)
SUM([Sales]) / SUM({ EXCLUDE [Category] : SUM([Sales]) })
-- DuckDB SQL equivalent
SELECT
  region,
  category,
  SUM(sales) AS category_sales,
  SUM(sales) / SUM(SUM(sales)) OVER (PARTITION BY region) AS share_of_region
FROM orders
GROUP BY region, category;

How can an INCLUDE LOD map to a pre-aggregated subquery?

An INCLUDE expression adds a finer dimension (such as Order ID) before applying an outer aggregation (such as AVG across orders in a region):

// Tableau INCLUDE LOD: Average order value per Region
AVG({ INCLUDE [Order ID] : SUM([Sales]) })
-- DuckDB SQL: Group by the included grain first, then average at the view grain
WITH order_totals AS (
  SELECT
    region,
    order_id,
    SUM(sales) AS order_revenue
  FROM orders
  GROUP BY region, order_id
)
SELECT
  region,
  AVG(order_revenue) AS avg_order_value
FROM order_totals
GROUP BY region;

Why do my DuckDB FIXED LOD numbers differ when a filter is applied in Tableau?

In Tableau's order of operations, standard dimension filters execute after FIXED LOD expressions, whereas Context Filters execute before FIXED LODs. In DuckDB, put Context Filters inside the inner FIXED CTE's WHERE clause, and put standard dimension filters in the outer query's WHERE clause.

Do I have to rewrite my .twbx to SQL just to view it without Tableau Desktop?

No. You can drop a .twbx or .twb file into the xmetric workbook inspector in your browser to list its data sources, sheets, and LOD formulas, save the packaged data files, and run formulas in the formula playground for free without installing desktop software (Tableau also offers a free local Desktop edition and free Tableau Reader on Windows and Mac). You can also open the workbook in the xmetric app and work with its sheets on shelves; not every dashboard will look identical to Tableau.

Can xmetric run Level of Detail expressions on CSV and Parquet files?

Yes. Import a CSV, Excel (.xlsx), or Parquet file into the xmetric app, write FIXED, INCLUDE, or EXCLUDE calculated fields, and drag them onto shelves. The file stays in your browser unless you save the workbook online. To test a single formula on sample data, use the formula playground, which shows the equivalent SQL.

Run your first LOD in the formula playground, then build the full view in the xmetric app.

What data does this worked example use?

The initial view uses authored synthetic data, not customer data or a captured agent session.

The example record lists its file, calculation or SQL and expected values. The live browser result is computed by xmetric’s existing engine or workbook inspector. Samples are excluded from own-data activation reporting.