<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>topic From Tableau to Databricks: Migrating KPI Dashboards with Metric Views in Community Articles</title>
    <link>https://community.databricks.com/t5/community-articles/from-tableau-to-databricks-migrating-kpi-dashboards-with-metric/m-p/158483#M1248</link>
    <description>&lt;P&gt;&lt;EM&gt;How to extract Tableau calculated fields, dimensions, and measures from a .twbx workbook and re-express them as a production-grade Databricks Metric View YAML — with the Sample Superstore dataset as a complete worked example, accelerated by AI coding assistants.&lt;/EM&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;TABLE width="624"&gt;&lt;TBODY&gt;&lt;TR&gt;&lt;TD width="208"&gt;&lt;P&gt;&lt;STRONG&gt;Dataset&lt;/STRONG&gt;&lt;/P&gt;&lt;P&gt;Sample Superstore&lt;/P&gt;&lt;/TD&gt;&lt;TD width="208"&gt;&lt;P&gt;&lt;STRONG&gt;Source&lt;/STRONG&gt;&lt;/P&gt;&lt;P&gt;KPI_Designs.twbx&lt;/P&gt;&lt;/TD&gt;&lt;TD width="208"&gt;&lt;P&gt;&lt;STRONG&gt;Platform&lt;/STRONG&gt;&lt;/P&gt;&lt;P&gt;Databricks Unity Catalog&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H1&gt;1. Introduction&lt;/H1&gt;&lt;P&gt;Tableau remains one of the most widely deployed BI tools in the enterprise, and its .twbx workbook format is a rich artefact: it bundles not just visualisations but also a full semantic layer — calculated fields, period-over-period logic, dimension hierarchies, and target thresholds — all expressed in Tableau's formula language.&lt;/P&gt;&lt;P&gt;Databricks Metric Views, available in Unity Catalog as part of the AI/BI surface, offer a YAML-native way to declare that same semantic layer inside your lakehouse. Once registered, a Metric View becomes a reusable, queryable object that powers Genie conversations, AI/BI Dashboards, and any SQL client connected to your warehouse — without rebuilding logic in every downstream tool.&lt;/P&gt;&lt;P&gt;This article walks through a complete migration using KPI_Designs.twbx — a publicly available Tableau workbook built on the Sample Superstore dataset — as the source, and produces a working version: 0.1 Metric View YAML that was validated live in a Databricks workspace. The translation itself was accelerated using an AI coding assistant; the techniques generalise to Claude, Cursor, GitHub Copilot, or Databricks Assistant / Genie Code.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H1&gt;2. Significance of Metric Views in Databricks&lt;/H1&gt;&lt;H2&gt;2.1&amp;nbsp; The Semantic Layer Problem&lt;/H2&gt;&lt;P&gt;Every analytics organisation eventually runs into the same issue: the same metric is calculated differently in different tools. The marketing team's Tableau workbook defines "Profit Ratio" as SUM(Profit)/SUM(Sales) for the current month. The finance team's Looker explore defines it as AVG(profit_margin) across all rows. Both are called "Profit Ratio" on executive dashboards. Neither team knows the other exists.&lt;/P&gt;&lt;P&gt;This is the semantic layer problem — the gap between raw data in a warehouse and the agreed-upon business definitions that should govern every report. Metric Views solve it by making those definitions a first-class citizen of the lakehouse, stored in Unity Catalog alongside the tables they describe.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H2&gt;2.2&amp;nbsp; What a Metric View Actually Is&lt;/H2&gt;&lt;P&gt;A Metric View is a Unity Catalog object defined in YAML. It references a source table, declares typed &lt;STRONG&gt;dimensions&lt;/STRONG&gt; (the axes by which you slice data), and declares &lt;STRONG&gt;measures&lt;/STRONG&gt; (the aggregations you care about). Measures can reference other measures using the MEASURE() function, enabling compound calculations without repeating SQL.&lt;/P&gt;&lt;P&gt;# Minimal metric view structure&lt;/P&gt;&lt;P&gt;version: 0.1&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;source: catalog.schema.table&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;dimensions:&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Order Date&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: order_date&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;measures:&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Sales&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: SUM(sales)&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Profit Ratio&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: SUM(profit) / NULLIF(SUM(sales), 0)&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H2&gt;2.3&amp;nbsp; Why They Matter&lt;/H2&gt;&lt;P&gt;Metric Views deliver four concrete benefits for data engineering teams:&lt;/P&gt;&lt;UL&gt;&lt;LI&gt;&lt;STRONG&gt;Single source of truth. &lt;/STRONG&gt;Business logic lives in one YAML file under version control, not scattered across Tableau calculated fields, dbt metrics, and ad-hoc SQL notebooks.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;Natural language querying. &lt;/STRONG&gt;Databricks Genie can reason over registered Metric Views, letting business users ask questions in plain English and receive answers backed by your governed definitions.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;AI/BI Dashboard compatibility. &lt;/STRONG&gt;Metric Views are the native data source for Databricks AI/BI Dashboards, enabling auto-generated visualisations with correct aggregation semantics.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;Governance and lineage. &lt;/STRONG&gt;Unity Catalog tracks Metric View dependencies, so column-level lineage runs from raw table through dimension/measure to dashboard cell.&lt;/LI&gt;&lt;/UL&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;TABLE width="624"&gt;&lt;TBODY&gt;&lt;TR&gt;&lt;TD width="624"&gt;&lt;P&gt;&lt;STRONG&gt;Key insight&lt;/STRONG&gt;&lt;/P&gt;&lt;P&gt;Migrating from Tableau is not just about moving charts — it is about promoting your semantic layer (the business logic inside calculated fields) from a proprietary workbook format into an open, versionable, lakehouse-native standard.&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H1&gt;3. Anatomy of a .twbx File&lt;/H1&gt;&lt;P&gt;A .twbx file is a ZIP archive. Unzipping it reveals a .twb XML document alongside any packaged data extracts (.hyper files) and images. The .twb is where all the metric logic lives.&lt;/P&gt;&lt;P&gt;# Inspect the archive contents&lt;/P&gt;&lt;P&gt;unzip -l KPI_Designs.twbx&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;# Output:&lt;/P&gt;&lt;P&gt;#&amp;nbsp;&amp;nbsp; KPI Designs.twb&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ← all metric logic in XML&lt;/P&gt;&lt;P&gt;#&amp;nbsp;&amp;nbsp; Data/TEMP_*.hyper&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ← packaged data extract&lt;/P&gt;&lt;P&gt;#&amp;nbsp;&amp;nbsp; Image/Mini.png&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ← workbook thumbnail&lt;/P&gt;&lt;P&gt;The .twb XML has a predictable structure. The elements relevant to metric migration are:&lt;/P&gt;&lt;UL&gt;&lt;LI&gt;&amp;lt;datasource&amp;gt;&amp;nbsp; — defines the source connection and all calculated fields&lt;/LI&gt;&lt;LI&gt;&amp;lt;column&amp;gt;&amp;nbsp; — each calculated field with its name, datatype, role (dimension/measure), and formula&lt;/LI&gt;&lt;LI&gt;&amp;lt;worksheet&amp;gt;&amp;nbsp; — individual sheet definitions that reference columns&lt;/LI&gt;&lt;LI&gt;&amp;lt;dashboard&amp;gt;&amp;nbsp; — layout containers that assemble worksheets&lt;/LI&gt;&lt;/UL&gt;&lt;P&gt;For KPI_Designs.twbx specifically, the workbook contains 50+ calculated fields covering Sales, Profit, Profit Ratio, and Customers — each with last-month, last-year, YTD, QTD, and running-total variants. These calculated fields are the raw material that needs to be translated into Metric View measures.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H1&gt;4. Mapping Tableau Concepts to Metric View YAML&lt;/H1&gt;&lt;H2&gt;4.1&amp;nbsp; Concept Mapping Table&lt;/H2&gt;&lt;P&gt;Most Tableau concepts have a clean Metric View equivalent, but a few — notably LOD expressions, table calculations, and parameters — require deliberate adaptation rather than direct translation.&lt;/P&gt;&lt;TABLE width="624"&gt;&lt;TBODY&gt;&lt;TR&gt;&lt;TD width="208"&gt;&lt;P&gt;&lt;STRONG&gt;Tableau Concept&lt;/STRONG&gt;&lt;/P&gt;&lt;/TD&gt;&lt;TD width="416"&gt;&lt;P&gt;&lt;STRONG&gt;Databricks Metric View Equivalent&lt;/STRONG&gt;&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD width="208"&gt;&lt;P&gt;&lt;STRONG&gt;Dimension field&lt;/STRONG&gt;&lt;/P&gt;&lt;/TD&gt;&lt;TD width="416"&gt;&lt;P&gt;dimensions[] entry — name plus expr referencing a column or SQL expression used to slice measures.&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD width="208"&gt;&lt;P&gt;&lt;STRONG&gt;Measure field&lt;/STRONG&gt;&lt;/P&gt;&lt;/TD&gt;&lt;TD width="416"&gt;&lt;P&gt;measures[] entry — name plus expr containing an aggregation such as SUM, COUNT DISTINCT, or AVG.&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD width="208"&gt;&lt;P&gt;&lt;STRONG&gt;Calculated dimension&lt;/STRONG&gt;&lt;/P&gt;&lt;/TD&gt;&lt;TD width="416"&gt;&lt;P&gt;dimensions[].expr with an arbitrary SQL expression, e.g. DATE_TRUNC('MONTH', order_date).&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD width="208"&gt;&lt;P&gt;&lt;STRONG&gt;Calculated measure&lt;/STRONG&gt;&lt;/P&gt;&lt;/TD&gt;&lt;TD width="416"&gt;&lt;P&gt;measures[].expr containing the aggregation, optionally referencing other measures via MEASURE(`name`).&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD width="208"&gt;&lt;P&gt;&lt;STRONG&gt;FIXED LOD expression&lt;/STRONG&gt;&lt;/P&gt;&lt;/TD&gt;&lt;TD width="416"&gt;&lt;P&gt;Scalar subquery works for global aggregates like {MAX([Order Date])}. Row-level FIXED LODs like {FIXED [Sub-Category] : SUM([Sales])} need window functions in a derived view or a separate aggregated table — they do not have a direct one-line equivalent.&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD width="208"&gt;&lt;P&gt;&lt;STRONG&gt;Table calculation&lt;/STRONG&gt;&lt;/P&gt;&lt;/TD&gt;&lt;TD width="416"&gt;&lt;P&gt;Window function inside the measure expr (e.g. SUM(...) OVER (ORDER BY ...)). Behaviour differs from Tableau, which computes after the visualisation partition is formed — validate results carefully.&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD width="208"&gt;&lt;P&gt;&lt;STRONG&gt;Parameter (user input)&lt;/STRONG&gt;&lt;/P&gt;&lt;/TD&gt;&lt;TD width="416"&gt;&lt;P&gt;No native equivalent. Either hardcode a sensible default in the measure (e.g. * 1.10 growth assumption) or expose the value as a filter applied at query/dashboard time in AI/BI.&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD width="208"&gt;&lt;P&gt;&lt;STRONG&gt;Date filter / TF field&lt;/STRONG&gt;&lt;/P&gt;&lt;/TD&gt;&lt;TD width="416"&gt;&lt;P&gt;CASE WHEN inside the measure expr with bounds computed via ADD_MONTHS and DATE_TRUNC against the latest data date.&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD width="208"&gt;&lt;P&gt;&lt;STRONG&gt;Set / group&lt;/STRONG&gt;&lt;/P&gt;&lt;/TD&gt;&lt;TD width="416"&gt;&lt;P&gt;Either a derived dimension with a CASE WHEN, or a join to a mapping table declared as a separate source.&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H2&gt;4.2&amp;nbsp; Translating Period Logic&lt;/H2&gt;&lt;P&gt;The most intricate part of the Superstore workbook is the period-over-period logic. Tableau achieves this with Level of Detail (LOD) expressions and boolean "time filter" calculated fields. In a Metric View, you replicate the global-anchor variant with CASE WHEN inside an aggregation, where the anchor date is computed via a scalar subquery against the source table.&lt;/P&gt;&lt;P&gt;# Tableau calculated field: Sales | LM&lt;/P&gt;&lt;P&gt;# IF [Date TF] THEN [Sales] END&lt;/P&gt;&lt;P&gt;# where [Date TF] checks if order_date falls in the most recent complete month&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;# Databricks Metric View equivalent:&lt;/P&gt;&lt;P&gt;- name: Sales LM&lt;/P&gt;&lt;P&gt;&amp;nbsp; expr: |&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; SUM(CASE&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; WHEN order_date &amp;gt;= ADD_MONTHS(&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; DATE_TRUNC('MONTH',&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (SELECT MAX(order_date) FROM default.sample_superstore)), -1)&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; AND order_date &amp;lt;&amp;nbsp; DATE_TRUNC('MONTH',&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (SELECT MAX(order_date) FROM default.sample_superstore))&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; THEN sales&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; END)&lt;/P&gt;&lt;P&gt;The scalar subquery resolves the anchor date dynamically at query time, which mirrors how Tableau's {MAX([Order Date])} LOD expression behaves at the data source level. The same pattern with ADD_MONTHS(..., -13) and ADD_MONTHS(..., -12) produces the same-month-last-year window.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H2&gt;4.3&amp;nbsp; Using MEASURE() for Derived Metrics&lt;/H2&gt;&lt;P&gt;Compound metrics like Profit Ratio or percentage-change deltas should reference already-defined measures rather than repeating aggregation SQL. Databricks provides the MEASURE() function for this purpose, both inside the YAML and when querying the view downstream:&lt;/P&gt;&lt;P&gt;# Profit Ratio — derived from two other measures&lt;/P&gt;&lt;P&gt;- name: Profit Ratio LM&lt;/P&gt;&lt;P&gt;&amp;nbsp; expr: MEASURE(`Profit LM`) / NULLIF(MEASURE(`Sales LM`), 0)&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;# Period-over-period delta&lt;/P&gt;&lt;P&gt;- name: Sales % Change LMLY&lt;/P&gt;&lt;P&gt;&amp;nbsp; expr: &amp;gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (MEASURE(`Sales LM`) - MEASURE(`Sales LY`))&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; / NULLIF(MEASURE(`Sales LY`), 0)&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;# Boolean status flag&lt;/P&gt;&lt;P&gt;- name: Sales Hit Target&lt;/P&gt;&lt;P&gt;&amp;nbsp; expr: &amp;gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; CASE WHEN MEASURE(`Sales LM`) &amp;gt; MEASURE(`Sales Target`)&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; THEN 1 ELSE 0 END&lt;/P&gt;&lt;TABLE width="624"&gt;&lt;TBODY&gt;&lt;TR&gt;&lt;TD width="624"&gt;&lt;P&gt;&lt;STRONG&gt;Backtick rule&lt;/STRONG&gt;&lt;/P&gt;&lt;P&gt;Measure names containing spaces must be wrapped in backticks inside MEASURE() references: MEASURE(`Sales LM`). Single-word names work either way, but use backticks consistently to avoid ambiguity.&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H2&gt;4.4&amp;nbsp; Accelerating the Translation with AI Coding Assistants&lt;/H2&gt;&lt;P&gt;Translating dozens of calculated fields by hand is tedious and error-prone. A modern shortcut is to delegate the translation to an AI coding assistant. The tool reads the .twb XML (or a textual export of the calculated fields), interprets the Tableau formula language, and emits the equivalent Databricks Metric View YAML — including the period-over-period CASE WHEN patterns shown above.&lt;/P&gt;&lt;P&gt;Several assistants work well for this task:&lt;/P&gt;&lt;UL&gt;&lt;LI&gt;&lt;STRONG&gt;Claude (Sonnet or Opus). &lt;/STRONG&gt;Strong at structured-format translation; paste the .twb XML and the target source table schema, and ask for the YAML directly. Handles nuanced cases like LOD expressions and target logic with a clear preamble.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;Cursor or GitHub Copilot. &lt;/STRONG&gt;IDE-integrated; useful when iterating on the YAML in your repo and refining individual measures with inline suggestions.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;Databricks Assistant / Genie Code. &lt;/STRONG&gt;Runs inside the Databricks notebook environment with awareness of your Unity Catalog schemas, making it the natural choice when the source table already lives in Databricks.&lt;/LI&gt;&lt;/UL&gt;&lt;P&gt;A high-leverage prompt template for this work:&lt;/P&gt;&lt;P&gt;I have a Tableau .twbx workbook (calculated field XML pasted below) built on a&lt;/P&gt;&lt;P&gt;table at &amp;lt;catalog&amp;gt;.&amp;lt;schema&amp;gt;.&amp;lt;table&amp;gt; with columns: &amp;lt;list&amp;gt;.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Generate a Databricks Metric View YAML (version: 0.1) that:&lt;/P&gt;&lt;OL&gt;&lt;LI&gt;1. Declares each calculated dimension and measure&lt;/LI&gt;&lt;LI&gt;2. Translates period-over-period logic using ADD_MONTHS + scalar subquery&lt;/LI&gt;&lt;/OL&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; anchored to MAX(order_date) in the source table&lt;/P&gt;&lt;OL&gt;&lt;LI&gt;3. Uses MEASURE(`name`) syntax for derived measures&lt;/LI&gt;&lt;LI&gt;4. Handles NULL division with NULLIF()&lt;/LI&gt;&lt;/OL&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Tableau calculated fields:&lt;/P&gt;&lt;P&gt;&amp;lt;paste the &amp;lt;column&amp;gt; elements from the .twb XML here&amp;gt;&lt;/P&gt;&lt;TABLE width="624"&gt;&lt;TBODY&gt;&lt;TR&gt;&lt;TD width="624"&gt;&lt;P&gt;&lt;STRONG&gt;Caveat&lt;/STRONG&gt;&lt;/P&gt;&lt;P&gt;AI-generated YAML still needs review. LLMs occasionally hallucinate column names, miss edge cases in date arithmetic, or invent dimension types that do not exist in the source. Always run DESCRIBE on the source table before applying the YAML, and validate the syntax in the Metric View editor — which highlights errors in real time.&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H1&gt;5. The Complete Metric View YAML&lt;/H1&gt;&lt;P&gt;Below is the full working YAML for the Superstore KPI workbook, validated in a live Databricks workspace (workspace.default schema, Serverless Starter Warehouse). It captures the four headline KPIs — Sales, Profit, Profit Ratio, and Customers — along with last-month / last-year variants, period-over-period deltas, year-to-date / quarter-to-date roll-ups, and target tracking.&lt;/P&gt;&lt;P&gt;version: 0.1&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;source: default.sample_superstore&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;# ── Dimensions ───────────────────────────────────────────────&lt;/P&gt;&lt;P&gt;dimensions:&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Order Date&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: order_date&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Order Month&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: DATE_TRUNC('MONTH', order_date)&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Order Quarter&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: DATE_TRUNC('QUARTER', order_date)&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Order Year&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: YEAR(order_date)&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Customer ID&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: customer_id&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Customer Name&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: customer_name&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Segment&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: segment&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Region&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: region&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: State/Province&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: state_province&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Category&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: category&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Sub-Category&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: sub_category&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Product Name&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: product_name&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Ship Mode&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: ship_mode&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;# ── Measures ─────────────────────────────────────────────────&lt;/P&gt;&lt;P&gt;measures:&lt;/P&gt;&lt;P&gt;&amp;nbsp; # Core&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Sales&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: SUM(sales)&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Profit&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: SUM(profit)&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Profit Ratio&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: SUM(profit) / NULLIF(SUM(sales), 0)&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Customers&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: COUNT(DISTINCT customer_id)&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Orders&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: COUNT(DISTINCT order_id)&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp; # Last Month&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Sales LM&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: |&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SUM(CASE WHEN order_date &amp;gt;= ADD_MONTHS(DATE_TRUNC('MONTH',&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (SELECT MAX(order_date) FROM default.sample_superstore)), -1)&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; AND order_date &amp;lt; DATE_TRUNC('MONTH',&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (SELECT MAX(order_date) FROM default.sample_superstore))&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; THEN sales END)&lt;/P&gt;&lt;P&gt;&amp;nbsp; # ... (Profit LM, Customers LM follow same pattern)&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp; # Period-over-period&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Sales % Change LMLY&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: &amp;gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (MEASURE(`Sales LM`) - MEASURE(`Sales LY`))&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; / NULLIF(MEASURE(`Sales LY`), 0)&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp; # Targets&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Sales Target&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: MEASURE(`Sales LY`) * 1.10&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Sales Hit Target&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: CASE WHEN MEASURE(`Sales LM`) &amp;gt; MEASURE(`Sales Target`) THEN 1 ELSE 0 END&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Profit Ratio Target&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: 0.15&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H1&gt;6. Registering the Metric View in Databricks&lt;/H1&gt;&lt;P&gt;With the YAML written, the next step is to register it as a first-class object in Unity Catalog. Databricks offers two paths: an interactive UI for one-off creation, and a programmatic path for CI/CD or repository-driven workflows.&lt;/P&gt;&lt;H2&gt;6.1&amp;nbsp; Creating via the Catalog Explorer UI&lt;/H2&gt;&lt;P&gt;In the Databricks workspace, navigate to &lt;STRONG&gt;Catalog → &lt;/STRONG&gt;select your target catalog and schema (here, workspace.default) → &lt;STRONG&gt;Create → Metric View&lt;/STRONG&gt;. You will be prompted for a name and location:&lt;/P&gt;&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Rojaboina_0-1780782791546.jpeg" style="width: 400px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/27616i56F1F3B416212E77/image-size/medium?v=v2&amp;amp;px=400" role="button" title="Rojaboina_0-1780782791546.jpeg" alt="Rojaboina_0-1780782791546.jpeg" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;EM&gt;Figure 1 — Create metric view dialog: name set to sample_superstore_metric_view, location workspace.default.&lt;/EM&gt;&lt;/P&gt;&lt;P&gt;After clicking Create, Databricks opens a side-by-side editor with the YAML on the right and a live UI preview / catalog tree on the left. Paste the YAML, choose a warehouse (Serverless Starter Warehouse works for development), and click Save. The editor validates syntax in real time and surfaces parse errors inline.&lt;/P&gt;&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Rojaboina_1-1780782791571.jpeg" style="width: 400px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/27617i2D5E33CC66A33975/image-size/medium?v=v2&amp;amp;px=400" role="button" title="Rojaboina_1-1780782791571.jpeg" alt="Rojaboina_1-1780782791571.jpeg" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;EM&gt;Figure 2 — YAML editor with the Superstore Metric View loaded. The Catalog Explorer panel on the left confirms the view registered in workspace.default; the YAML on the right shows dimensions and measures resolved against default.sample_superstore.&lt;/EM&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H2&gt;6.2&amp;nbsp; Creating Programmatically&lt;/H2&gt;&lt;P&gt;For teams managing metric definitions in a Git repository, Metric Views can be created and updated via the Databricks CLI or REST API. Both accept the same YAML payload, making CI/CD straightforward:&lt;/P&gt;&lt;P&gt;# Databricks CLI&lt;/P&gt;&lt;P&gt;databricks metric-views create \&lt;/P&gt;&lt;P&gt;&amp;nbsp; --catalog workspace \&lt;/P&gt;&lt;P&gt;&amp;nbsp; --schema default \&lt;/P&gt;&lt;P&gt;&amp;nbsp; --name sample_superstore_metric_view \&lt;/P&gt;&lt;P&gt;&amp;nbsp; --definition-file superstore_metrics_view.yaml&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;# Or use SQL DDL in a notebook / SQL editor:&lt;/P&gt;&lt;P&gt;CREATE OR REPLACE VIEW workspace.default.sample_superstore_metric_view&lt;/P&gt;&lt;P&gt;WITH METRICS&lt;/P&gt;&lt;P&gt;LANGUAGE YAML&lt;/P&gt;&lt;P&gt;AS $$&lt;/P&gt;&lt;P&gt;&amp;lt;paste YAML body here&amp;gt;&lt;/P&gt;&lt;P&gt;$$;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H1&gt;7. Using the Metric View&lt;/H1&gt;&lt;H2&gt;7.1&amp;nbsp; Querying with SQL&lt;/H2&gt;&lt;P&gt;A registered Metric View is queryable from any SQL surface in Databricks. The important difference from a regular view is that &lt;STRONG&gt;measures must be invoked via the MEASURE() function&lt;/STRONG&gt; — they are not plain columns. Dimensions are referenced by name (with backticks if they contain spaces).&lt;/P&gt;&lt;P&gt;-- Sales and Profit Ratio by Region, current vs last month&lt;/P&gt;&lt;P&gt;SELECT&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; `Region`,&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; MEASURE(`Sales`)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; AS sales,&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; MEASURE(`Sales LM`)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; AS sales_last_month,&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; MEASURE(`Sales % Change LMLY`)&amp;nbsp;&amp;nbsp; AS sales_pct_change,&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; MEASURE(`Profit Ratio`)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; AS profit_ratio,&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; MEASURE(`Profit Ratio Below Target`) AS below_target_flag&lt;/P&gt;&lt;P&gt;FROM workspace.default.sample_superstore_metric_view&lt;/P&gt;&lt;P&gt;GROUP BY `Region`&lt;/P&gt;&lt;P&gt;ORDER BY MEASURE(`Sales`) DESC;&lt;/P&gt;&lt;P&gt;Note that ORDER BY against a measure must also use the MEASURE() wrapper. Aliases declared in the SELECT cannot be referenced in ORDER BY with the measure name alone — Databricks resolves measure references through the function call.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H2&gt;7.2&amp;nbsp; Natural Language with Genie&lt;/H2&gt;&lt;P&gt;Register the Metric View as a data source in a Genie space, and business users can ask questions in plain English:&lt;/P&gt;&lt;UL&gt;&lt;LI&gt;"Which region had the highest profit ratio last month?"&lt;/LI&gt;&lt;LI&gt;"Show me sales hit-target performance by category."&lt;/LI&gt;&lt;LI&gt;"What is the year-over-year sales change by sub-category?"&lt;/LI&gt;&lt;/UL&gt;&lt;P&gt;Genie uses the declared measure definitions to construct correct SQL — including the MEASURE() wrappers — eliminating the risk of ad-hoc aggregation errors. Because measure semantics are encoded in the YAML, the answers Genie produces are consistent across users and questions.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H2&gt;7.3&amp;nbsp; AI/BI Dashboards&lt;/H2&gt;&lt;P&gt;Connect an AI/BI Dashboard directly to the Metric View. Databricks auto-suggests visualisation types based on measure characteristics and generates chart configurations from dimension and measure names. The KPI cards, sparklines, and period-comparison deltas from the original Tableau workbook can be recreated in minutes — and any change to the underlying YAML propagates to every dashboard built on it.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H1&gt;8. Best Practices and Gotchas&lt;/H1&gt;&lt;H2&gt;8.1&amp;nbsp; Normalise Column Names Before Building the View&lt;/H2&gt;&lt;P&gt;The Sample Superstore Excel ships with headers containing spaces, slashes, and dashes (Order Date, Sub-Category, State/Province). Convert all column names to lowercase snake_case during ingestion so every expr field can reference them without backtick escaping. A one-time rename (order_date, sub_category, state_province) permanently eliminates that friction.&lt;/P&gt;&lt;H2&gt;8.2&amp;nbsp; Scalar Subqueries Beat Window Functions for Date Anchors&lt;/H2&gt;&lt;P&gt;The pattern (SELECT MAX(order_date) FROM source_table) inside a CASE WHEN gives you a dynamic "latest date" anchor without needing a calendar table or a parameter. This is the Databricks equivalent of Tableau's {MAX([Order Date])} LOD expression — and unlike MAX(...) OVER (), it nests cleanly inside aggregations.&lt;/P&gt;&lt;H2&gt;8.3&amp;nbsp; Use ADD_MONTHS, Not DATE_ADD, for Month Arithmetic&lt;/H2&gt;&lt;P&gt;For shifting dates by N months, always use ADD_MONTHS(date, n) — not DATE_ADD(date, n * 30). The latter breaks on months with different day counts and produces wrong results at month boundaries. ADD_MONTHS handles leap years and 28/29/30/31-day months correctly.&lt;/P&gt;&lt;H2&gt;8.4&amp;nbsp; Wrap Measure References in Backticks&lt;/H2&gt;&lt;P&gt;Inside both YAML expressions and SQL queries, measure names containing spaces require backticks: MEASURE(`Sales LM`), MEASURE(`Profit Ratio`). Missing backticks produce a parse error that is easy to miss in a long YAML. Use them consistently even for single-word names to avoid the inconsistency.&lt;/P&gt;&lt;H2&gt;8.5&amp;nbsp; Tableau Table Calculations Do Not Translate Directly&lt;/H2&gt;&lt;P&gt;Tableau's table calculations (RUNNING_SUM, LOOKUP, RANK) are computed after aggregation, row by row within a visualisation partition. They have no direct Metric View equivalent. Running totals can be approximated with window functions (SUM(...) OVER (ORDER BY ...)), but these behave differently when filters or groupings change. Flag these fields for manual review during migration — they are the most likely source of result discrepancies between the Tableau original and the Databricks port.&lt;/P&gt;&lt;H2&gt;8.6&amp;nbsp; Validate Sample Aggregates Against the Source Workbook&lt;/H2&gt;&lt;P&gt;Before declaring the migration done, pick three or four measures (Sales YTD, Profit Ratio, % Change LMLY) and compare numbers between the Tableau workbook and the Databricks Metric View for an identical filter. A 0.1% discrepancy usually points to a date-boundary edge case; a &amp;gt;5% discrepancy usually points to a translation error in an LOD or table calculation.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H1&gt;9. Conclusion&lt;/H1&gt;&lt;P&gt;Tableau workbooks are a rich source of institutional knowledge about how a business wants its metrics defined. The calculated fields, date logic, and target thresholds inside a .twbx file represent years of alignment between data and business teams.&lt;/P&gt;&lt;P&gt;Databricks Metric Views offer a path to preserve that knowledge in an open, lakehouse-native format — one that is queryable by SQL, understandable by Genie, and reusable across every downstream tool connected to Unity Catalog. The migration process follows a clear pattern: extract calculated fields from the .twb XML, translate Tableau LOD expressions into scalar subqueries, map dimension and measure roles to YAML structure, and normalise source column names. AI coding assistants compress what used to be a multi-week project into a single afternoon of focused review.&lt;/P&gt;&lt;P&gt;The result is a governed semantic layer that any tool or AI agent in the Databricks ecosystem can consume consistently — and that survives the inevitable next BI tool migration without rebuilding business logic from scratch.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;TABLE width="624"&gt;&lt;TBODY&gt;&lt;TR&gt;&lt;TD width="624"&gt;&lt;P&gt;&lt;STRONG&gt;Resources&lt;/STRONG&gt;&lt;/P&gt;&lt;P&gt;Sample Superstore data: &lt;A href="https://public.tableau.com/app/learn/sample-data" target="_blank" rel="noopener"&gt;https://public.tableau.com/app/learn/sample-data&lt;/A&gt; KPI Designs workbook (Stanke): &lt;A href="https://public.tableau.com/app/profile/stanke/viz/KPIDesigns/KPIDesign" target="_blank" rel="noopener"&gt;https://public.tableau.com/app/profile/stanke/viz/KPIDesigns/KPIDesign&lt;/A&gt; Databricks AI/BI documentation: &lt;A href="https://docs.databricks.com/en/ai-bi/index.html" target="_blank" rel="noopener"&gt;https://docs.databricks.com/en/ai-bi/index.html&lt;/A&gt;&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;P&gt;&amp;nbsp;#Databricks #Metric Views #Tableau&lt;/P&gt;</description>
    <pubDate>Sat, 06 Jun 2026 21:55:33 GMT</pubDate>
    <dc:creator>Rojaboina</dc:creator>
    <dc:date>2026-06-06T21:55:33Z</dc:date>
    <item>
      <title>From Tableau to Databricks: Migrating KPI Dashboards with Metric Views</title>
      <link>https://community.databricks.com/t5/community-articles/from-tableau-to-databricks-migrating-kpi-dashboards-with-metric/m-p/158483#M1248</link>
      <description>&lt;P&gt;&lt;EM&gt;How to extract Tableau calculated fields, dimensions, and measures from a .twbx workbook and re-express them as a production-grade Databricks Metric View YAML — with the Sample Superstore dataset as a complete worked example, accelerated by AI coding assistants.&lt;/EM&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;TABLE width="624"&gt;&lt;TBODY&gt;&lt;TR&gt;&lt;TD width="208"&gt;&lt;P&gt;&lt;STRONG&gt;Dataset&lt;/STRONG&gt;&lt;/P&gt;&lt;P&gt;Sample Superstore&lt;/P&gt;&lt;/TD&gt;&lt;TD width="208"&gt;&lt;P&gt;&lt;STRONG&gt;Source&lt;/STRONG&gt;&lt;/P&gt;&lt;P&gt;KPI_Designs.twbx&lt;/P&gt;&lt;/TD&gt;&lt;TD width="208"&gt;&lt;P&gt;&lt;STRONG&gt;Platform&lt;/STRONG&gt;&lt;/P&gt;&lt;P&gt;Databricks Unity Catalog&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H1&gt;1. Introduction&lt;/H1&gt;&lt;P&gt;Tableau remains one of the most widely deployed BI tools in the enterprise, and its .twbx workbook format is a rich artefact: it bundles not just visualisations but also a full semantic layer — calculated fields, period-over-period logic, dimension hierarchies, and target thresholds — all expressed in Tableau's formula language.&lt;/P&gt;&lt;P&gt;Databricks Metric Views, available in Unity Catalog as part of the AI/BI surface, offer a YAML-native way to declare that same semantic layer inside your lakehouse. Once registered, a Metric View becomes a reusable, queryable object that powers Genie conversations, AI/BI Dashboards, and any SQL client connected to your warehouse — without rebuilding logic in every downstream tool.&lt;/P&gt;&lt;P&gt;This article walks through a complete migration using KPI_Designs.twbx — a publicly available Tableau workbook built on the Sample Superstore dataset — as the source, and produces a working version: 0.1 Metric View YAML that was validated live in a Databricks workspace. The translation itself was accelerated using an AI coding assistant; the techniques generalise to Claude, Cursor, GitHub Copilot, or Databricks Assistant / Genie Code.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H1&gt;2. Significance of Metric Views in Databricks&lt;/H1&gt;&lt;H2&gt;2.1&amp;nbsp; The Semantic Layer Problem&lt;/H2&gt;&lt;P&gt;Every analytics organisation eventually runs into the same issue: the same metric is calculated differently in different tools. The marketing team's Tableau workbook defines "Profit Ratio" as SUM(Profit)/SUM(Sales) for the current month. The finance team's Looker explore defines it as AVG(profit_margin) across all rows. Both are called "Profit Ratio" on executive dashboards. Neither team knows the other exists.&lt;/P&gt;&lt;P&gt;This is the semantic layer problem — the gap between raw data in a warehouse and the agreed-upon business definitions that should govern every report. Metric Views solve it by making those definitions a first-class citizen of the lakehouse, stored in Unity Catalog alongside the tables they describe.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H2&gt;2.2&amp;nbsp; What a Metric View Actually Is&lt;/H2&gt;&lt;P&gt;A Metric View is a Unity Catalog object defined in YAML. It references a source table, declares typed &lt;STRONG&gt;dimensions&lt;/STRONG&gt; (the axes by which you slice data), and declares &lt;STRONG&gt;measures&lt;/STRONG&gt; (the aggregations you care about). Measures can reference other measures using the MEASURE() function, enabling compound calculations without repeating SQL.&lt;/P&gt;&lt;P&gt;# Minimal metric view structure&lt;/P&gt;&lt;P&gt;version: 0.1&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;source: catalog.schema.table&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;dimensions:&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Order Date&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: order_date&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;measures:&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Sales&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: SUM(sales)&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Profit Ratio&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: SUM(profit) / NULLIF(SUM(sales), 0)&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H2&gt;2.3&amp;nbsp; Why They Matter&lt;/H2&gt;&lt;P&gt;Metric Views deliver four concrete benefits for data engineering teams:&lt;/P&gt;&lt;UL&gt;&lt;LI&gt;&lt;STRONG&gt;Single source of truth. &lt;/STRONG&gt;Business logic lives in one YAML file under version control, not scattered across Tableau calculated fields, dbt metrics, and ad-hoc SQL notebooks.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;Natural language querying. &lt;/STRONG&gt;Databricks Genie can reason over registered Metric Views, letting business users ask questions in plain English and receive answers backed by your governed definitions.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;AI/BI Dashboard compatibility. &lt;/STRONG&gt;Metric Views are the native data source for Databricks AI/BI Dashboards, enabling auto-generated visualisations with correct aggregation semantics.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;Governance and lineage. &lt;/STRONG&gt;Unity Catalog tracks Metric View dependencies, so column-level lineage runs from raw table through dimension/measure to dashboard cell.&lt;/LI&gt;&lt;/UL&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;TABLE width="624"&gt;&lt;TBODY&gt;&lt;TR&gt;&lt;TD width="624"&gt;&lt;P&gt;&lt;STRONG&gt;Key insight&lt;/STRONG&gt;&lt;/P&gt;&lt;P&gt;Migrating from Tableau is not just about moving charts — it is about promoting your semantic layer (the business logic inside calculated fields) from a proprietary workbook format into an open, versionable, lakehouse-native standard.&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H1&gt;3. Anatomy of a .twbx File&lt;/H1&gt;&lt;P&gt;A .twbx file is a ZIP archive. Unzipping it reveals a .twb XML document alongside any packaged data extracts (.hyper files) and images. The .twb is where all the metric logic lives.&lt;/P&gt;&lt;P&gt;# Inspect the archive contents&lt;/P&gt;&lt;P&gt;unzip -l KPI_Designs.twbx&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;# Output:&lt;/P&gt;&lt;P&gt;#&amp;nbsp;&amp;nbsp; KPI Designs.twb&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ← all metric logic in XML&lt;/P&gt;&lt;P&gt;#&amp;nbsp;&amp;nbsp; Data/TEMP_*.hyper&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ← packaged data extract&lt;/P&gt;&lt;P&gt;#&amp;nbsp;&amp;nbsp; Image/Mini.png&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ← workbook thumbnail&lt;/P&gt;&lt;P&gt;The .twb XML has a predictable structure. The elements relevant to metric migration are:&lt;/P&gt;&lt;UL&gt;&lt;LI&gt;&amp;lt;datasource&amp;gt;&amp;nbsp; — defines the source connection and all calculated fields&lt;/LI&gt;&lt;LI&gt;&amp;lt;column&amp;gt;&amp;nbsp; — each calculated field with its name, datatype, role (dimension/measure), and formula&lt;/LI&gt;&lt;LI&gt;&amp;lt;worksheet&amp;gt;&amp;nbsp; — individual sheet definitions that reference columns&lt;/LI&gt;&lt;LI&gt;&amp;lt;dashboard&amp;gt;&amp;nbsp; — layout containers that assemble worksheets&lt;/LI&gt;&lt;/UL&gt;&lt;P&gt;For KPI_Designs.twbx specifically, the workbook contains 50+ calculated fields covering Sales, Profit, Profit Ratio, and Customers — each with last-month, last-year, YTD, QTD, and running-total variants. These calculated fields are the raw material that needs to be translated into Metric View measures.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H1&gt;4. Mapping Tableau Concepts to Metric View YAML&lt;/H1&gt;&lt;H2&gt;4.1&amp;nbsp; Concept Mapping Table&lt;/H2&gt;&lt;P&gt;Most Tableau concepts have a clean Metric View equivalent, but a few — notably LOD expressions, table calculations, and parameters — require deliberate adaptation rather than direct translation.&lt;/P&gt;&lt;TABLE width="624"&gt;&lt;TBODY&gt;&lt;TR&gt;&lt;TD width="208"&gt;&lt;P&gt;&lt;STRONG&gt;Tableau Concept&lt;/STRONG&gt;&lt;/P&gt;&lt;/TD&gt;&lt;TD width="416"&gt;&lt;P&gt;&lt;STRONG&gt;Databricks Metric View Equivalent&lt;/STRONG&gt;&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD width="208"&gt;&lt;P&gt;&lt;STRONG&gt;Dimension field&lt;/STRONG&gt;&lt;/P&gt;&lt;/TD&gt;&lt;TD width="416"&gt;&lt;P&gt;dimensions[] entry — name plus expr referencing a column or SQL expression used to slice measures.&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD width="208"&gt;&lt;P&gt;&lt;STRONG&gt;Measure field&lt;/STRONG&gt;&lt;/P&gt;&lt;/TD&gt;&lt;TD width="416"&gt;&lt;P&gt;measures[] entry — name plus expr containing an aggregation such as SUM, COUNT DISTINCT, or AVG.&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD width="208"&gt;&lt;P&gt;&lt;STRONG&gt;Calculated dimension&lt;/STRONG&gt;&lt;/P&gt;&lt;/TD&gt;&lt;TD width="416"&gt;&lt;P&gt;dimensions[].expr with an arbitrary SQL expression, e.g. DATE_TRUNC('MONTH', order_date).&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD width="208"&gt;&lt;P&gt;&lt;STRONG&gt;Calculated measure&lt;/STRONG&gt;&lt;/P&gt;&lt;/TD&gt;&lt;TD width="416"&gt;&lt;P&gt;measures[].expr containing the aggregation, optionally referencing other measures via MEASURE(`name`).&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD width="208"&gt;&lt;P&gt;&lt;STRONG&gt;FIXED LOD expression&lt;/STRONG&gt;&lt;/P&gt;&lt;/TD&gt;&lt;TD width="416"&gt;&lt;P&gt;Scalar subquery works for global aggregates like {MAX([Order Date])}. Row-level FIXED LODs like {FIXED [Sub-Category] : SUM([Sales])} need window functions in a derived view or a separate aggregated table — they do not have a direct one-line equivalent.&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD width="208"&gt;&lt;P&gt;&lt;STRONG&gt;Table calculation&lt;/STRONG&gt;&lt;/P&gt;&lt;/TD&gt;&lt;TD width="416"&gt;&lt;P&gt;Window function inside the measure expr (e.g. SUM(...) OVER (ORDER BY ...)). Behaviour differs from Tableau, which computes after the visualisation partition is formed — validate results carefully.&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD width="208"&gt;&lt;P&gt;&lt;STRONG&gt;Parameter (user input)&lt;/STRONG&gt;&lt;/P&gt;&lt;/TD&gt;&lt;TD width="416"&gt;&lt;P&gt;No native equivalent. Either hardcode a sensible default in the measure (e.g. * 1.10 growth assumption) or expose the value as a filter applied at query/dashboard time in AI/BI.&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD width="208"&gt;&lt;P&gt;&lt;STRONG&gt;Date filter / TF field&lt;/STRONG&gt;&lt;/P&gt;&lt;/TD&gt;&lt;TD width="416"&gt;&lt;P&gt;CASE WHEN inside the measure expr with bounds computed via ADD_MONTHS and DATE_TRUNC against the latest data date.&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD width="208"&gt;&lt;P&gt;&lt;STRONG&gt;Set / group&lt;/STRONG&gt;&lt;/P&gt;&lt;/TD&gt;&lt;TD width="416"&gt;&lt;P&gt;Either a derived dimension with a CASE WHEN, or a join to a mapping table declared as a separate source.&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H2&gt;4.2&amp;nbsp; Translating Period Logic&lt;/H2&gt;&lt;P&gt;The most intricate part of the Superstore workbook is the period-over-period logic. Tableau achieves this with Level of Detail (LOD) expressions and boolean "time filter" calculated fields. In a Metric View, you replicate the global-anchor variant with CASE WHEN inside an aggregation, where the anchor date is computed via a scalar subquery against the source table.&lt;/P&gt;&lt;P&gt;# Tableau calculated field: Sales | LM&lt;/P&gt;&lt;P&gt;# IF [Date TF] THEN [Sales] END&lt;/P&gt;&lt;P&gt;# where [Date TF] checks if order_date falls in the most recent complete month&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;# Databricks Metric View equivalent:&lt;/P&gt;&lt;P&gt;- name: Sales LM&lt;/P&gt;&lt;P&gt;&amp;nbsp; expr: |&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; SUM(CASE&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; WHEN order_date &amp;gt;= ADD_MONTHS(&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; DATE_TRUNC('MONTH',&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (SELECT MAX(order_date) FROM default.sample_superstore)), -1)&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; AND order_date &amp;lt;&amp;nbsp; DATE_TRUNC('MONTH',&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (SELECT MAX(order_date) FROM default.sample_superstore))&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; THEN sales&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; END)&lt;/P&gt;&lt;P&gt;The scalar subquery resolves the anchor date dynamically at query time, which mirrors how Tableau's {MAX([Order Date])} LOD expression behaves at the data source level. The same pattern with ADD_MONTHS(..., -13) and ADD_MONTHS(..., -12) produces the same-month-last-year window.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H2&gt;4.3&amp;nbsp; Using MEASURE() for Derived Metrics&lt;/H2&gt;&lt;P&gt;Compound metrics like Profit Ratio or percentage-change deltas should reference already-defined measures rather than repeating aggregation SQL. Databricks provides the MEASURE() function for this purpose, both inside the YAML and when querying the view downstream:&lt;/P&gt;&lt;P&gt;# Profit Ratio — derived from two other measures&lt;/P&gt;&lt;P&gt;- name: Profit Ratio LM&lt;/P&gt;&lt;P&gt;&amp;nbsp; expr: MEASURE(`Profit LM`) / NULLIF(MEASURE(`Sales LM`), 0)&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;# Period-over-period delta&lt;/P&gt;&lt;P&gt;- name: Sales % Change LMLY&lt;/P&gt;&lt;P&gt;&amp;nbsp; expr: &amp;gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (MEASURE(`Sales LM`) - MEASURE(`Sales LY`))&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; / NULLIF(MEASURE(`Sales LY`), 0)&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;# Boolean status flag&lt;/P&gt;&lt;P&gt;- name: Sales Hit Target&lt;/P&gt;&lt;P&gt;&amp;nbsp; expr: &amp;gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; CASE WHEN MEASURE(`Sales LM`) &amp;gt; MEASURE(`Sales Target`)&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; THEN 1 ELSE 0 END&lt;/P&gt;&lt;TABLE width="624"&gt;&lt;TBODY&gt;&lt;TR&gt;&lt;TD width="624"&gt;&lt;P&gt;&lt;STRONG&gt;Backtick rule&lt;/STRONG&gt;&lt;/P&gt;&lt;P&gt;Measure names containing spaces must be wrapped in backticks inside MEASURE() references: MEASURE(`Sales LM`). Single-word names work either way, but use backticks consistently to avoid ambiguity.&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H2&gt;4.4&amp;nbsp; Accelerating the Translation with AI Coding Assistants&lt;/H2&gt;&lt;P&gt;Translating dozens of calculated fields by hand is tedious and error-prone. A modern shortcut is to delegate the translation to an AI coding assistant. The tool reads the .twb XML (or a textual export of the calculated fields), interprets the Tableau formula language, and emits the equivalent Databricks Metric View YAML — including the period-over-period CASE WHEN patterns shown above.&lt;/P&gt;&lt;P&gt;Several assistants work well for this task:&lt;/P&gt;&lt;UL&gt;&lt;LI&gt;&lt;STRONG&gt;Claude (Sonnet or Opus). &lt;/STRONG&gt;Strong at structured-format translation; paste the .twb XML and the target source table schema, and ask for the YAML directly. Handles nuanced cases like LOD expressions and target logic with a clear preamble.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;Cursor or GitHub Copilot. &lt;/STRONG&gt;IDE-integrated; useful when iterating on the YAML in your repo and refining individual measures with inline suggestions.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;Databricks Assistant / Genie Code. &lt;/STRONG&gt;Runs inside the Databricks notebook environment with awareness of your Unity Catalog schemas, making it the natural choice when the source table already lives in Databricks.&lt;/LI&gt;&lt;/UL&gt;&lt;P&gt;A high-leverage prompt template for this work:&lt;/P&gt;&lt;P&gt;I have a Tableau .twbx workbook (calculated field XML pasted below) built on a&lt;/P&gt;&lt;P&gt;table at &amp;lt;catalog&amp;gt;.&amp;lt;schema&amp;gt;.&amp;lt;table&amp;gt; with columns: &amp;lt;list&amp;gt;.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Generate a Databricks Metric View YAML (version: 0.1) that:&lt;/P&gt;&lt;OL&gt;&lt;LI&gt;1. Declares each calculated dimension and measure&lt;/LI&gt;&lt;LI&gt;2. Translates period-over-period logic using ADD_MONTHS + scalar subquery&lt;/LI&gt;&lt;/OL&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; anchored to MAX(order_date) in the source table&lt;/P&gt;&lt;OL&gt;&lt;LI&gt;3. Uses MEASURE(`name`) syntax for derived measures&lt;/LI&gt;&lt;LI&gt;4. Handles NULL division with NULLIF()&lt;/LI&gt;&lt;/OL&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Tableau calculated fields:&lt;/P&gt;&lt;P&gt;&amp;lt;paste the &amp;lt;column&amp;gt; elements from the .twb XML here&amp;gt;&lt;/P&gt;&lt;TABLE width="624"&gt;&lt;TBODY&gt;&lt;TR&gt;&lt;TD width="624"&gt;&lt;P&gt;&lt;STRONG&gt;Caveat&lt;/STRONG&gt;&lt;/P&gt;&lt;P&gt;AI-generated YAML still needs review. LLMs occasionally hallucinate column names, miss edge cases in date arithmetic, or invent dimension types that do not exist in the source. Always run DESCRIBE on the source table before applying the YAML, and validate the syntax in the Metric View editor — which highlights errors in real time.&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H1&gt;5. The Complete Metric View YAML&lt;/H1&gt;&lt;P&gt;Below is the full working YAML for the Superstore KPI workbook, validated in a live Databricks workspace (workspace.default schema, Serverless Starter Warehouse). It captures the four headline KPIs — Sales, Profit, Profit Ratio, and Customers — along with last-month / last-year variants, period-over-period deltas, year-to-date / quarter-to-date roll-ups, and target tracking.&lt;/P&gt;&lt;P&gt;version: 0.1&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;source: default.sample_superstore&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;# ── Dimensions ───────────────────────────────────────────────&lt;/P&gt;&lt;P&gt;dimensions:&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Order Date&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: order_date&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Order Month&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: DATE_TRUNC('MONTH', order_date)&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Order Quarter&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: DATE_TRUNC('QUARTER', order_date)&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Order Year&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: YEAR(order_date)&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Customer ID&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: customer_id&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Customer Name&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: customer_name&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Segment&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: segment&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Region&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: region&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: State/Province&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: state_province&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Category&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: category&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Sub-Category&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: sub_category&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Product Name&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: product_name&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Ship Mode&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: ship_mode&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;# ── Measures ─────────────────────────────────────────────────&lt;/P&gt;&lt;P&gt;measures:&lt;/P&gt;&lt;P&gt;&amp;nbsp; # Core&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Sales&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: SUM(sales)&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Profit&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: SUM(profit)&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Profit Ratio&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: SUM(profit) / NULLIF(SUM(sales), 0)&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Customers&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: COUNT(DISTINCT customer_id)&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Orders&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: COUNT(DISTINCT order_id)&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp; # Last Month&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Sales LM&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: |&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SUM(CASE WHEN order_date &amp;gt;= ADD_MONTHS(DATE_TRUNC('MONTH',&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (SELECT MAX(order_date) FROM default.sample_superstore)), -1)&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; AND order_date &amp;lt; DATE_TRUNC('MONTH',&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (SELECT MAX(order_date) FROM default.sample_superstore))&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; THEN sales END)&lt;/P&gt;&lt;P&gt;&amp;nbsp; # ... (Profit LM, Customers LM follow same pattern)&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp; # Period-over-period&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Sales % Change LMLY&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: &amp;gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (MEASURE(`Sales LM`) - MEASURE(`Sales LY`))&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; / NULLIF(MEASURE(`Sales LY`), 0)&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp; # Targets&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Sales Target&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: MEASURE(`Sales LY`) * 1.10&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Sales Hit Target&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: CASE WHEN MEASURE(`Sales LM`) &amp;gt; MEASURE(`Sales Target`) THEN 1 ELSE 0 END&lt;/P&gt;&lt;P&gt;&amp;nbsp; - name: Profit Ratio Target&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr: 0.15&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H1&gt;6. Registering the Metric View in Databricks&lt;/H1&gt;&lt;P&gt;With the YAML written, the next step is to register it as a first-class object in Unity Catalog. Databricks offers two paths: an interactive UI for one-off creation, and a programmatic path for CI/CD or repository-driven workflows.&lt;/P&gt;&lt;H2&gt;6.1&amp;nbsp; Creating via the Catalog Explorer UI&lt;/H2&gt;&lt;P&gt;In the Databricks workspace, navigate to &lt;STRONG&gt;Catalog → &lt;/STRONG&gt;select your target catalog and schema (here, workspace.default) → &lt;STRONG&gt;Create → Metric View&lt;/STRONG&gt;. You will be prompted for a name and location:&lt;/P&gt;&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Rojaboina_0-1780782791546.jpeg" style="width: 400px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/27616i56F1F3B416212E77/image-size/medium?v=v2&amp;amp;px=400" role="button" title="Rojaboina_0-1780782791546.jpeg" alt="Rojaboina_0-1780782791546.jpeg" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;EM&gt;Figure 1 — Create metric view dialog: name set to sample_superstore_metric_view, location workspace.default.&lt;/EM&gt;&lt;/P&gt;&lt;P&gt;After clicking Create, Databricks opens a side-by-side editor with the YAML on the right and a live UI preview / catalog tree on the left. Paste the YAML, choose a warehouse (Serverless Starter Warehouse works for development), and click Save. The editor validates syntax in real time and surfaces parse errors inline.&lt;/P&gt;&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Rojaboina_1-1780782791571.jpeg" style="width: 400px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/27617i2D5E33CC66A33975/image-size/medium?v=v2&amp;amp;px=400" role="button" title="Rojaboina_1-1780782791571.jpeg" alt="Rojaboina_1-1780782791571.jpeg" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;EM&gt;Figure 2 — YAML editor with the Superstore Metric View loaded. The Catalog Explorer panel on the left confirms the view registered in workspace.default; the YAML on the right shows dimensions and measures resolved against default.sample_superstore.&lt;/EM&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H2&gt;6.2&amp;nbsp; Creating Programmatically&lt;/H2&gt;&lt;P&gt;For teams managing metric definitions in a Git repository, Metric Views can be created and updated via the Databricks CLI or REST API. Both accept the same YAML payload, making CI/CD straightforward:&lt;/P&gt;&lt;P&gt;# Databricks CLI&lt;/P&gt;&lt;P&gt;databricks metric-views create \&lt;/P&gt;&lt;P&gt;&amp;nbsp; --catalog workspace \&lt;/P&gt;&lt;P&gt;&amp;nbsp; --schema default \&lt;/P&gt;&lt;P&gt;&amp;nbsp; --name sample_superstore_metric_view \&lt;/P&gt;&lt;P&gt;&amp;nbsp; --definition-file superstore_metrics_view.yaml&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;# Or use SQL DDL in a notebook / SQL editor:&lt;/P&gt;&lt;P&gt;CREATE OR REPLACE VIEW workspace.default.sample_superstore_metric_view&lt;/P&gt;&lt;P&gt;WITH METRICS&lt;/P&gt;&lt;P&gt;LANGUAGE YAML&lt;/P&gt;&lt;P&gt;AS $$&lt;/P&gt;&lt;P&gt;&amp;lt;paste YAML body here&amp;gt;&lt;/P&gt;&lt;P&gt;$$;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H1&gt;7. Using the Metric View&lt;/H1&gt;&lt;H2&gt;7.1&amp;nbsp; Querying with SQL&lt;/H2&gt;&lt;P&gt;A registered Metric View is queryable from any SQL surface in Databricks. The important difference from a regular view is that &lt;STRONG&gt;measures must be invoked via the MEASURE() function&lt;/STRONG&gt; — they are not plain columns. Dimensions are referenced by name (with backticks if they contain spaces).&lt;/P&gt;&lt;P&gt;-- Sales and Profit Ratio by Region, current vs last month&lt;/P&gt;&lt;P&gt;SELECT&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; `Region`,&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; MEASURE(`Sales`)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; AS sales,&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; MEASURE(`Sales LM`)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; AS sales_last_month,&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; MEASURE(`Sales % Change LMLY`)&amp;nbsp;&amp;nbsp; AS sales_pct_change,&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; MEASURE(`Profit Ratio`)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; AS profit_ratio,&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; MEASURE(`Profit Ratio Below Target`) AS below_target_flag&lt;/P&gt;&lt;P&gt;FROM workspace.default.sample_superstore_metric_view&lt;/P&gt;&lt;P&gt;GROUP BY `Region`&lt;/P&gt;&lt;P&gt;ORDER BY MEASURE(`Sales`) DESC;&lt;/P&gt;&lt;P&gt;Note that ORDER BY against a measure must also use the MEASURE() wrapper. Aliases declared in the SELECT cannot be referenced in ORDER BY with the measure name alone — Databricks resolves measure references through the function call.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H2&gt;7.2&amp;nbsp; Natural Language with Genie&lt;/H2&gt;&lt;P&gt;Register the Metric View as a data source in a Genie space, and business users can ask questions in plain English:&lt;/P&gt;&lt;UL&gt;&lt;LI&gt;"Which region had the highest profit ratio last month?"&lt;/LI&gt;&lt;LI&gt;"Show me sales hit-target performance by category."&lt;/LI&gt;&lt;LI&gt;"What is the year-over-year sales change by sub-category?"&lt;/LI&gt;&lt;/UL&gt;&lt;P&gt;Genie uses the declared measure definitions to construct correct SQL — including the MEASURE() wrappers — eliminating the risk of ad-hoc aggregation errors. Because measure semantics are encoded in the YAML, the answers Genie produces are consistent across users and questions.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H2&gt;7.3&amp;nbsp; AI/BI Dashboards&lt;/H2&gt;&lt;P&gt;Connect an AI/BI Dashboard directly to the Metric View. Databricks auto-suggests visualisation types based on measure characteristics and generates chart configurations from dimension and measure names. The KPI cards, sparklines, and period-comparison deltas from the original Tableau workbook can be recreated in minutes — and any change to the underlying YAML propagates to every dashboard built on it.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H1&gt;8. Best Practices and Gotchas&lt;/H1&gt;&lt;H2&gt;8.1&amp;nbsp; Normalise Column Names Before Building the View&lt;/H2&gt;&lt;P&gt;The Sample Superstore Excel ships with headers containing spaces, slashes, and dashes (Order Date, Sub-Category, State/Province). Convert all column names to lowercase snake_case during ingestion so every expr field can reference them without backtick escaping. A one-time rename (order_date, sub_category, state_province) permanently eliminates that friction.&lt;/P&gt;&lt;H2&gt;8.2&amp;nbsp; Scalar Subqueries Beat Window Functions for Date Anchors&lt;/H2&gt;&lt;P&gt;The pattern (SELECT MAX(order_date) FROM source_table) inside a CASE WHEN gives you a dynamic "latest date" anchor without needing a calendar table or a parameter. This is the Databricks equivalent of Tableau's {MAX([Order Date])} LOD expression — and unlike MAX(...) OVER (), it nests cleanly inside aggregations.&lt;/P&gt;&lt;H2&gt;8.3&amp;nbsp; Use ADD_MONTHS, Not DATE_ADD, for Month Arithmetic&lt;/H2&gt;&lt;P&gt;For shifting dates by N months, always use ADD_MONTHS(date, n) — not DATE_ADD(date, n * 30). The latter breaks on months with different day counts and produces wrong results at month boundaries. ADD_MONTHS handles leap years and 28/29/30/31-day months correctly.&lt;/P&gt;&lt;H2&gt;8.4&amp;nbsp; Wrap Measure References in Backticks&lt;/H2&gt;&lt;P&gt;Inside both YAML expressions and SQL queries, measure names containing spaces require backticks: MEASURE(`Sales LM`), MEASURE(`Profit Ratio`). Missing backticks produce a parse error that is easy to miss in a long YAML. Use them consistently even for single-word names to avoid the inconsistency.&lt;/P&gt;&lt;H2&gt;8.5&amp;nbsp; Tableau Table Calculations Do Not Translate Directly&lt;/H2&gt;&lt;P&gt;Tableau's table calculations (RUNNING_SUM, LOOKUP, RANK) are computed after aggregation, row by row within a visualisation partition. They have no direct Metric View equivalent. Running totals can be approximated with window functions (SUM(...) OVER (ORDER BY ...)), but these behave differently when filters or groupings change. Flag these fields for manual review during migration — they are the most likely source of result discrepancies between the Tableau original and the Databricks port.&lt;/P&gt;&lt;H2&gt;8.6&amp;nbsp; Validate Sample Aggregates Against the Source Workbook&lt;/H2&gt;&lt;P&gt;Before declaring the migration done, pick three or four measures (Sales YTD, Profit Ratio, % Change LMLY) and compare numbers between the Tableau workbook and the Databricks Metric View for an identical filter. A 0.1% discrepancy usually points to a date-boundary edge case; a &amp;gt;5% discrepancy usually points to a translation error in an LOD or table calculation.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H1&gt;9. Conclusion&lt;/H1&gt;&lt;P&gt;Tableau workbooks are a rich source of institutional knowledge about how a business wants its metrics defined. The calculated fields, date logic, and target thresholds inside a .twbx file represent years of alignment between data and business teams.&lt;/P&gt;&lt;P&gt;Databricks Metric Views offer a path to preserve that knowledge in an open, lakehouse-native format — one that is queryable by SQL, understandable by Genie, and reusable across every downstream tool connected to Unity Catalog. The migration process follows a clear pattern: extract calculated fields from the .twb XML, translate Tableau LOD expressions into scalar subqueries, map dimension and measure roles to YAML structure, and normalise source column names. AI coding assistants compress what used to be a multi-week project into a single afternoon of focused review.&lt;/P&gt;&lt;P&gt;The result is a governed semantic layer that any tool or AI agent in the Databricks ecosystem can consume consistently — and that survives the inevitable next BI tool migration without rebuilding business logic from scratch.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;TABLE width="624"&gt;&lt;TBODY&gt;&lt;TR&gt;&lt;TD width="624"&gt;&lt;P&gt;&lt;STRONG&gt;Resources&lt;/STRONG&gt;&lt;/P&gt;&lt;P&gt;Sample Superstore data: &lt;A href="https://public.tableau.com/app/learn/sample-data" target="_blank" rel="noopener"&gt;https://public.tableau.com/app/learn/sample-data&lt;/A&gt; KPI Designs workbook (Stanke): &lt;A href="https://public.tableau.com/app/profile/stanke/viz/KPIDesigns/KPIDesign" target="_blank" rel="noopener"&gt;https://public.tableau.com/app/profile/stanke/viz/KPIDesigns/KPIDesign&lt;/A&gt; Databricks AI/BI documentation: &lt;A href="https://docs.databricks.com/en/ai-bi/index.html" target="_blank" rel="noopener"&gt;https://docs.databricks.com/en/ai-bi/index.html&lt;/A&gt;&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;P&gt;&amp;nbsp;#Databricks #Metric Views #Tableau&lt;/P&gt;</description>
      <pubDate>Sat, 06 Jun 2026 21:55:33 GMT</pubDate>
      <guid>https://community.databricks.com/t5/community-articles/from-tableau-to-databricks-migrating-kpi-dashboards-with-metric/m-p/158483#M1248</guid>
      <dc:creator>Rojaboina</dc:creator>
      <dc:date>2026-06-06T21:55:33Z</dc:date>
    </item>
  </channel>
</rss>

