> ## Documentation Index
> Fetch the complete documentation index at: https://docs.credibledata.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrate from Power BI

> Convert your Power BI tabular model and DAX measures into governed Malloy semantic models

Your Power BI semantic model holds the definitions your business runs on — tables, relationships, and DAX measures. Credible reads the model definition, re-expresses its DAX logic as Malloy, and — where you connect it — validates each measure against Power BI's own query engine before you cut over.

## What Credible Reads

Your **tabular model** — tables, columns, relationships, and DAX measures — from its text definition in **TMDL** (Tabular Model Definition Language, the folder-based format inside a PBIP project) or the older TMSL. Hand the agent those files and it can translate. Connecting the official **Power BI Modeling MCP server** (which loads TMDL) or the **XMLA endpoint** is optional: it lets the agent load the model live and run DAX for validation.

## What Comes Across

The everyday modeling carries over. Here's how the bigger pieces land — and where they get better:

| In Power BI                          | In Credible                                                                                                  |
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------ |
| Tabular model, tables, relationships | Malloy **sources** and joins                                                                                 |
| DAX measures & calculated columns    | Measures and dimensions — `CALCULATE`'s filter-context gymnastics become plain, readable filtered aggregates |
| Time-intelligence (`TOTALYTD`, …)    | Rebuilt as explicit time-grain truncation and window views                                                   |
| RLS roles                            | **Fine-grained access control in the model**, versioned and enforced on every surface                        |
| Descriptions                         | `#(doc)` / `#(index)`, indexed by the [Context Engine](/how-to/analyzing/overview)                           |
| Reports & dashboards                 | Rebuilt as [data apps](/how-to/analyzing/data-apps) or [notebooks](/how-to/analyzing/workspaces)             |

## The Migration Flow

Credible [reads](/how-to/migrating/overview) the TMDL, translates tables and relationships to Malloy sources and joins and DAX measures to Malloy measures, enriches with `#(doc)`/`#(index)` tags, and — where you connect it — validates row-by-row using the **ExecuteQueries** API to run the original DAX and diff it against the migrated result.

## What Credible Handles

* **Filter context and `CALCULATE`** — a simple `CALCULATE([Total], Status="shipped")` is an equivalent filtered aggregate in Malloy. DAX that *removes* or *overrides* context (`ALL`, `REMOVEFILTERS`) has no ambient equivalent and is re-modeled as an `all()`/level-of-detail aggregate or a separate query — explicitly, never guessed.
* **Time-intelligence** (`TOTALYTD`, `SAMEPERIODLASTYEAR`) depends on a marked date table and the visual's current filter. Malloy has no ambient filter context, so year-to-date is not a measure — it becomes an explicit cumulative view driven by the query's date grain.
* **Implicit measures** — auto-aggregations that were never stored as named measures are materialized explicitly, so parity checks don't miss them.

## Before & After

A TMDL table (relationships live in a separate `relationships.tmdl`):

```tmdl theme={"languages":{"custom":["/languages/motly.tmGrammar.json","/languages/malloy.tmGrammar.json"]}}
table Sales
	column OrderID
		dataType: int64
		isKey
		sourceColumn: order_id

	/// Date the order was placed
	column OrderDate
		dataType: dateTime
		sourceColumn: created_at

	column Status
		dataType: string
		sourceColumn: order_status

	/// Total revenue in USD
	measure 'Total Revenue' = SUM('Sales'[Amount])
		formatString: \$#,0.00

	/// Revenue from shipped orders only (filter context)
	measure 'Shipped Revenue' =
			CALCULATE ( [Total Revenue], 'Sales'[Status] = "shipped" )

	/// Share of revenue that shipped
	measure 'Shipped Revenue %' =
			DIVIDE ( [Shipped Revenue], [Total Revenue] )
		formatString: 0.0%

	/// Year-to-date revenue
	measure 'Revenue YTD' = TOTALYTD ( [Total Revenue], 'Calendar'[Date] )
```

```malloy theme={"languages":{"custom":["/languages/motly.tmGrammar.json","/languages/malloy.tmGrammar.json"]}}
source: orders is conn.table('sales.orders') extend {
  primary_key: order_id
  join_one: customers is conn.table('sales.customers') on customer_id

  dimension:
    #(doc) Date the order was placed
    order_date is created_at::date

    #(doc) Order fulfillment status
    #(index)
    status is order_status

  measure:
    #(doc) Total revenue in USD
    # currency
    total_revenue is sum(amount)

    #(doc) Revenue from shipped orders only
    # currency
    shipped_revenue is sum(amount) { where: status = 'shipped' }

    #(doc) Share of revenue that shipped
    # percent
    shipped_revenue_pct is shipped_revenue / total_revenue

  view:
    #(doc) Year-to-date revenue — cumulative by month
    revenue_ytd is {
      group_by: order_date.month
      aggregate: total_revenue
      calculate: running_total is sum_cumulative(total_revenue) {
        partition_by: order_date.year
        order_by: order_date.month asc
      }
    }
}
```

`CALCULATE` with a simple predicate becomes a `{ where: … }` filtered measure; `TOTALYTD` — which relied on ambient filter context — becomes an explicit cumulative view. The `formatString` masks map to `# currency`/`# percent`.

**More than a reformat.** The migration leaves DAX filter-context complexity behind for aggregates that are [correct by construction](/how-to/migrating/overview#more-than-a-reformat), and your logic becomes portable, AI-discoverable code instead of a Power BI artifact. [See what you gain →](/how-to/migrating/overview#more-than-a-reformat)

## Next Steps

<CardGroup cols={2}>
  <Card title="Migration Overview" icon="diagram-project" color="#5C7A93" href="/how-to/migrating/overview">
    The method behind every migration
  </Card>

  <Card title="Access Control" icon="filter" color="#94793A" href="/how-to/modeling/fine-grained-acls">
    Re-establish RLS as governed access rules
  </Card>
</CardGroup>
