Power Query M code converts to SQL by treating each step in a let…in chain (or each shared query in a section document) as a node in a flow: Table.SelectRows becomes a filter, Table.AddColumn becomes a calculated column, Table.Group becomes an aggregation, Table.NestedJoin plus Table.ExpandTableColumn becomes a join, and so on. The fastest way to get started today is to copy the M code straight out of the Advanced Editor and paste it in — no .pbix file needed.
- Power Query's structure comes from data dependencies between let-bindings, not a visual canvas — edges are inferred from which earlier steps a binding's expression references.
- Copying M code out of the Advanced Editor and pasting it in is the fastest way to convert a query today; extracting M directly from a .pbix or .pbit file is a separate, more involved problem.
- Table.Group, Table.NestedJoin, and Table.SelectRows map to standard SQL operations; custom function calls and List-based transforms are flagged for manual review rather than guessed at.
Why Power Query conversion works differently
Tableau Prep, Alteryx, and SSIS all store their logic as a node graph in XML or JSON. Power Query stores its logic as M code — a sequence of let-bindings, each one usually built on the step before it. The 'flow' has to be reconstructed from that code rather than read directly off a canvas.
In practice this reconstruction is a data-dependency analysis: whichever earlier binding names appear inside a step's expression become that step's inputs. Most queries are a straight line of steps, but branches and joins between two prior steps work the same way and are picked up naturally.
Which M functions map directly to SQL
A core set of Table.* functions covers the majority of real-world Power Query steps.
- Sql.Database, Csv.Document, Excel.Workbook, Web.Contents → source tables
- Table.SelectRows → WHERE clause
- Table.AddColumn → calculated column
- Table.RenameColumns, Table.RemoveColumns → renames and column pruning
- Table.Group → GROUP BY with Sum/Count/Average/Min/Max aggregations
- Table.NestedJoin + Table.ExpandTableColumn → JOIN (inner, left, right, or full, based on the configured join kind)
- Table.Combine → UNION
- Table.Distinct, Table.FirstN → DISTINCT and LIMIT/TOP
What still needs manual review
Table.Pivot has no direct SQL equivalent in a general form and is flagged rather than guessed at. Offset-based functions like Table.Skip and Table.Range don't map to a clean SQL clause on their own. Custom function calls and List-based or record-based transforms are preserved as-is for an engineer to translate, since their behavior depends on logic that lives outside the table-transformation pattern the rest of the mapping covers.
None of these gaps should block the conversion — the rest of the query still converts normally, with the unmapped step called out explicitly so it doesn't get lost.
Where Deflows fits
Deflows accepts Power Query as pasted M code (copied straight from the Advanced Editor) or as a .pq/.m file, and parses it into the same internal flow structure used for Tableau Prep, Alteryx, and SSIS. Extracting M code directly out of a .pbix or .pbit file's internal DataMashup format is a separate, binary-format problem not covered by the current version — the paste-based workflow is the fastest path available today.
Questions teams ask
Do I need a .pbix file to convert Power Query logic?
No. Copying the M code out of the Advanced Editor (in Excel's Get & Transform or Power BI Desktop) and pasting it in works today. Extracting M directly from inside a .pbix or .pbit file is a different, harder problem — that binary format isn't parsed in the current version.
What happens if a query references another query that wasn't pasted in?
It's flagged as an external reference rather than causing the conversion to fail. If you paste in a full section document with multiple shared queries that reference each other, those cross-query references are resolved automatically within the same document.