# Tableau Custom SQL to Power BI: Native Queries, Query Folding, and What to Rewrite

> Custom SQL becomes a Power Query native query. What folds and what does not, why Value.NativeQuery matters, and which statements should become a view instead.

- Canonical: https://getantares.io/tableau-custom-sql-to-power-bi/
- Published: 2026-02-18
- Updated: 2026-07-28

---

Tableau runs custom SQL as a subquery: it has to wrap your statement in a `SELECT` of its own, because it cannot inject `WHERE` and `GROUP BY` clauses into SQL you wrote. Power BI does the same thing with a native database query, entered in the *SQL statement* box under Advanced options on the connector. The difference that matters is what happens to everything you do afterwards: in Power BI, how you paste the SQL decides whether later steps run on the database or on your own machine.

## Custom SQL to Power Query conversion table

| Tableau | Power BI equivalent | Notes |
| --- | --- | --- |
| Custom SQL query | Native database query in the *SQL statement* box | Simplest route, but subsequent steps stop folding |
| Custom SQL plus later filtering | `Value.NativeQuery(Source, "…", null, [EnableFolding = true])` | Keeps folding alive on supported connectors |
| Parameter inside custom SQL | Power Query parameter | Both replace literal values only |
| Parameter driven by a user control | Dynamic M query parameter bound to a field | DirectQuery sources only |
| Initial SQL | No equivalent | Move the logic into the query, a view, or the source |
| Joins on top of custom SQL | Model relationships, or a database view | Usually a sign the SQL should become a view |
| Live connection on custom SQL | DirectQuery with a native query | All partitions must query a single source |
| Extract on custom SQL | Import mode | The query runs at refresh, not at view time |

## Why the SQL statement box quietly costs you performance

[Query folding](https://learn.microsoft.com/en-us/power-query/query-folding-basics) is the mechanism that translates your Power Query steps into the data source's own language and pushes them down to it. Folding is full, partial or absent, and the difference is not cosmetic: without it, Power BI retrieves the raw result and filters, groups and sorts it locally.

A native query pasted into the *SQL statement* box returns a table, but Microsoft's own documentation is explicit that this route means your query "doesn't take advantage of any query folding from subsequent query steps". [The documented alternative](https://learn.microsoft.com/en-us/power-query/native-query-folding) is to connect at the database level, then call `Value.NativeQuery` with `EnableFolding = true`. Power Query then wraps your SQL as a subquery and folds later steps into it, which is the same shape Tableau uses, except that here you can watch it happen in the query plan.

That option is connector-specific. It is documented for Amazon Redshift, Dataverse with enhanced compute, Google BigQuery, PostgreSQL, SAP HANA, Snowflake and SQL Server. Anywhere else, assume the pasted statement is the end of folding, and put the filtering inside the SQL itself. Folding also decides whether [incremental refresh](/convert-hyper-files-to-power-bi/) is worth configuring at all, since a policy whose filter does not fold pulls the whole table anyway.

## Not all custom SQL should survive the migration

Tableau's documentation states plainly that using custom SQL can affect workbook performance, and a workbook estate full of it usually means the modelling was done in SQL because the data source could not express it. Power BI moves that work somewhere else, so the migration is a good moment to sort custom SQL into three buckets.

| What the SQL does | Where it belongs in Power BI |
| --- | --- |
| Window functions, complex joins, security predicates | Stays SQL, ideally as a database view |
| Renaming, type casting, filtering, pivoting | Power Query steps that fold |
| Denormalising several tables into one wide result | Model tables plus [relationships](/tableau-blending-to-power-bi-relationships/) |
| Unions of near-identical tables | Power Query append, or a view |

A view is the underrated option. It moves the statement to where the DBA can see and index it, gives every report the same definition, and leaves Power Query with a plain table to fold against. It also removes the duplicate-column trap Tableau warns about, where the same field name arriving from two joined tables makes the field unusable.

## Custom SQL under DirectQuery

When the Tableau workbook was live rather than extracted, the equivalent is DirectQuery over the same statement. Two things then change. All partitions of the model have to query a single source, so a native query that quietly reached across databases needs restructuring first. And report users can no longer be given free-form parameters: the supported route is a [dynamic M query parameter](https://learn.microsoft.com/en-us/power-bi/connect-data/desktop-dynamic-m-query-parameters), where a field in a table is bound to an M parameter so a slicer selection reaches the source query.

Treat that binding carefully. Microsoft documents an injection risk when parameter values are concatenated into query text, and recommends consuming them through operations that fold, a stored procedure, or the source language's own parameter mechanism instead. A Tableau parameter dropped into custom SQL carries the same exposure; it is simply less visible there.

## What Antares does with custom SQL

The Analyzer records which data sources are backed by custom SQL rather than tables, and what each one connects to. That matters for scoping because those data sources are not a mechanical conversion: each is a modelling decision about whether the statement becomes a view, a set of folded Power Query steps, or a relationship in the model.

Conversion itself covers the dashboard layer. The rule the product follows is the same one that applies here: where a mapping is ambiguous, it is flagged rather than guessed, because a silently rewritten query is far more expensive to find in testing than a flagged one is to review. [Run the free Analyzer](https://try.getantares.io) to see where custom SQL sits in your own estate.

Related reading: [data sources to Power BI datasets](/tableau-data-sources-to-power-bi-datasets/) and [calculated fields to DAX](/tableau-calculated-fields-to-dax/). Primary sources: Tableau's [custom SQL documentation](https://help.tableau.com/current/pro/desktop/en-us/customsql.htm) and Microsoft's guides to [native database queries](https://learn.microsoft.com/en-us/power-query/native-database-query) and the [Value.NativeQuery function](https://learn.microsoft.com/en-us/powerquery-m/value-nativequery).

## Related resources

- [Tableau to Power BI Migration Guide](/tableau-to-power-bi-migration/)
- [Data Sources to Power BI Datasets](/tableau-data-sources-to-power-bi-datasets/)
- [Blending to Power BI Relationships](/tableau-blending-to-power-bi-relationships/)
- [Hyper Files to Power BI](/convert-hyper-files-to-power-bi/)

## FAQ

### What is the Power BI equivalent of Tableau custom SQL?

A native database query, entered in the SQL statement box under Advanced options on the connector. Power Query runs it much as Tableau does, wrapping it as a subquery. For anything that needs later filtering to reach the database, use Value.NativeQuery with EnableFolding set to true instead.

### Does custom SQL stop query folding in Power BI?

Through the SQL statement box, yes: Microsoft documents that subsequent query steps do not fold. Value.NativeQuery with EnableFolding = true keeps folding for later steps, and is documented for Amazon Redshift, Dataverse with enhanced compute, Google BigQuery, PostgreSQL, SAP HANA, Snowflake and SQL Server.

### Can Tableau parameters inside custom SQL be converted?

Yes. A Power Query parameter covers the same ground, since both replace literal values only. To let a report user drive the value, bind a field to a dynamic M query parameter, which works for DirectQuery sources. Avoid concatenating user values into query text, which Microsoft flags as an injection risk.

### Should custom SQL be kept or rebuilt during migration?

Keep SQL for what the database does best, such as window functions, complex joins and security predicates, ideally as a view. Move renaming, casting, filtering and pivoting into Power Query steps that fold, and turn denormalising statements into model tables with relationships.
