Free tool runs in your browser

Tableau to SQL converter

Paste a calculated field. The xmetric engine runs it the way Tableau does, writes the SQL for it, then runs that SQL on the same rows and tells you whether the two agree. Nothing is uploaded.

Updated · Checked on the xmetric engine

Convert a formula

Paste a calculated field. Fields: [id], [Order Date], [Region], [Customer], [Category], [Sales], [Profit].

In the view (for aggregates and level of detail)

Sample: 10 orders

Run on 10 rows: this SQL returns the same values as the Tableau formula.

SQL (DuckDB)

SELECT
  (CASE WHEN ("Profit" > 0.0) THEN 'Gain' ELSE 'Loss' END) AS result
FROM input
ProfitTableauSQL
18GainGain
−24LossLoss
12GainGain
40GainGain
90GainGain
LossLoss
−15LossLoss
9GainGain
4GainGain
102GainGain

SQL notes and traps for each function: IF

The 10 sample rows
idOrder DateRegionCustomerCategorySalesProfit
12024-01-05EastAna SilvaFurniture12018
22024-01-19EastBen OdeTechnology300-24
32024-02-02EastAna SilvaOffice8012
42024-02-14WestCai LinFurniture20040
52024-03-03WestDev RaoTechnology45090
62024-03-21WestCai LinOffice60null
72024-04-08SouthEve MossTechnology150-15
82024-04-30SouthEve MossFurniture909
92024-05-12NorthFinn KaiOffice404
102024-06-01NorthGus PellTechnology510102

What do common formulas look like in SQL?

Each one was converted by the engine and run both ways on the 10 sample rows.

How does an IF become a CASE?

Tableau

IF [Profit] > 0 THEN "Gain" ELSE "Loss" END

Run on 10 rows: this SQL returns the same values as the Tableau formula.

SQL (DuckDB)

SELECT
  (CASE WHEN ("Profit" > 0.0) THEN 'Gain' ELSE 'Loss' END) AS result
FROM input
ProfitTableauSQL
18GainGain
−24LossLoss
12GainGain
40GainGain
90GainGain
LossLoss
−15LossLoss
9GainGain
4GainGain
102GainGain

SQL notes and traps for each function: IF

How do I count weeks between dates like Tableau?

Tableau

DATEDIFF('week', [Order Date], #2024-06-01#)

Run on 10 rows: this SQL returns the same values as the Tableau formula.

SQL (DuckDB)

SELECT
  CAST((floor((date_diff('day', DATE '1970-01-01', DATE '2024-06-01') + 4) / 7.0) - floor((date_diff('day', DATE '1970-01-01', CAST("Order Date" AS DATE)) + 4) / 7.0)) AS BIGINT) AS result
FROM input
Order DateTableauSQL
2024-01-052121
2024-01-191919
2024-02-021717
2024-02-141515
2024-03-031212
2024-03-211010
2024-04-0877
2024-04-3044
2024-05-1222
2024-06-0100

SQL notes and traps for each function: DATEDIFF

How does ZN with a division translate?

Tableau

ZN([Profit]) / [Sales]

Run on 10 rows: this SQL returns the same values as the Tableau formula.

SQL (DuckDB)

SELECT (coalesce("Profit", 0) / NULLIF("Sales", 0)) AS result
FROM input
SalesProfitTableauSQL
120180.150.15
300−24−0.08−0.08
80120.150.15
200400.20.2
450900.20.2
6000
150−15−0.1−0.1
9090.10.1
4040.10.1
5101020.20.2

SQL notes and traps for each function: ZN

What is DATETRUNC by quarter in SQL?

Tableau

DATETRUNC('quarter', [Order Date])

Run on 10 rows: this SQL returns the same values as the Tableau formula.

SQL (DuckDB)

SELECT
  CAST(date_trunc('quarter', "Order Date") AS DATE) AS result
FROM input
Order DateTableauSQL
2024-01-052024-01-012024-01-01
2024-01-192024-01-012024-01-01
2024-02-022024-01-012024-01-01
2024-02-142024-01-012024-01-01
2024-03-032024-01-012024-01-01
2024-03-212024-01-012024-01-01
2024-04-082024-04-012024-04-01
2024-04-302024-04-012024-04-01
2024-05-122024-04-012024-04-01
2024-06-012024-04-012024-04-01

SQL notes and traps for each function: DATETRUNC

What does a profit ratio look like per region?

Tableau

SUM([Profit]) / SUM([Sales])

In the view: Region

Run on 10 rows: this SQL returns the same values as the Tableau formula.

SQL (DuckDB)

WITH "marks" AS (
  SELECT "Region" COLLATE NOCASE AS "Region"
  FROM input
  GROUP BY "Region"
), "per_mark" AS (
  SELECT
    "Region" COLLATE NOCASE AS "Region",
    (sum("Profit") / NULLIF(sum("Sales"), 0)) AS "result"
  FROM input
  GROUP BY "Region"
)
SELECT m."Region" AS "Region", o1."result" AS "result"
FROM "marks" m LEFT JOIN "per_mark" o1 ON m."Region" IS NOT DISTINCT FROM o1."Region"
ORDER BY m."Region"

Text compares with COLLATE NOCASE because Tableau compares text without regard to case. Drop it if your data is all one case.

RegionTableauSQL
East0.010.01
North0.190.19
South−0.03−0.03
West0.180.18

SQL notes and traps for each function: SUM

How does a FIXED level of detail become SQL?

Tableau

{ FIXED [Region] : SUM([Sales]) }

In the view: Region

Run on 10 rows: this SQL returns the same values as the Tableau formula.

SQL (DuckDB)

WITH "lod_0" AS (
  SELECT
    "Region" COLLATE NOCASE AS "lod_key_0",
    sum("Sales") AS "value"
  FROM input
  GROUP BY "lod_key_0"
), "rows_1" AS (
  SELECT b.*, l."value" AS "lod_a81fe6"
  FROM input b LEFT JOIN "lod_0" l ON b."Region" COLLATE NOCASE IS NOT DISTINCT FROM l."lod_key_0"
), "marks" AS (
  SELECT "Region" COLLATE NOCASE AS "Region"
  FROM "rows_1"
  GROUP BY "Region"
), "per_mark" AS (
  SELECT
    "Region" COLLATE NOCASE AS "Region",
    MIN("lod_a81fe6") AS "result"
  FROM "rows_1"
  GROUP BY "Region"
)
SELECT m."Region" AS "Region", o1."result" AS "result"
FROM "marks" m LEFT JOIN "per_mark" o1 ON m."Region" IS NOT DISTINCT FROM o1."Region"
ORDER BY m."Region"

Text compares with COLLATE NOCASE because Tableau compares text without regard to case. Drop it if your data is all one case.

RegionTableauSQL
East500500
North550550
South240240
West710710

SQL notes and traps for each function: SUM FIXED

What is COUNTD in SQL?

Tableau

COUNTD([Customer])

In the view: Category

Run on 10 rows: this SQL returns the same values as the Tableau formula.

SQL (DuckDB)

WITH "marks" AS (
  SELECT "Category" COLLATE NOCASE AS "Category"
  FROM input
  GROUP BY "Category"
), "per_mark" AS (
  SELECT
    "Category" COLLATE NOCASE AS "Category",
    count(DISTINCT lower("Customer" COLLATE NOCASE)) AS "result"
  FROM input
  GROUP BY "Category"
)
SELECT m."Category" AS "Category", o1."result" AS "result"
FROM "marks" m LEFT JOIN "per_mark" o1 ON m."Category" IS NOT DISTINCT FROM o1."Category"
ORDER BY m."Category"

Text compares with COLLATE NOCASE because Tableau compares text without regard to case. Drop it if your data is all one case.

CategoryTableauSQL
Furniture33
Office33
Technology44

SQL notes and traps for each function: COUNTD

What do people ask about converting Tableau to SQL?

Which SQL does it write?

DuckDB SQL that reads a table named input. Most of it runs unchanged on Postgres. For Snowflake and BigQuery, each function's reference page gives the dialect notes.

Does it handle level of detail expressions?

Yes. FIXED, INCLUDE and EXCLUDE become common table expressions at the grain they ask for, joined back to the marks of the view you tick.

What about table calculations like RUNNING_SUM or RANK?

Those run over the marks of a view in a set order, so the converter shows the Tableau result and links each function's page, which gives the window-function SQL.

Why does the SQL say COLLATE NOCASE?

Tableau compares text without regard to case, so east and East are one region. The collation keeps that behaviour in SQL.

Is my file uploaded?

No. The engine and DuckDB run in your browser tab; your CSV never leaves your device.

Where is this documented?