Home Course home Curriculum Learner dashboard Skill tree Business projects Datasets Final project
🧹 Module 3 · Power Query & Data Transformation Advanced LESSON 23 / 72

Parameters, Query Dependencies & Intro to M

34 min 5 quiz questions 1 practical challenge 6 syllabus topics

The step up from clicking buttons to building something maintainable. Parameters make queries portable, references and groups keep a growing file navigable, the Advanced Editor shows you what has been happening all along, and custom functions let you write a transformation once and apply it everywhere.

Lesson 23 of 72 · Power Query & Data Transformation 32%
01

Learning Objective

Everything so far has been achievable through the interface. This lesson is about the structure that makes a twenty-query file comprehensible rather than a maze.

By the end of this lesson you will be able to
  • Create and use parameters for paths, environments and thresholds
  • Organise queries with references, duplicates and groups, and know the difference
  • Read the Advanced Editor confidently and understand M's let/in structure
  • Write a custom function and invoke it across a table
  • Structure a query file so someone else can maintain it
02

Why This Matters

A file with three queries needs no organisation. A file with twenty-five does, and every real project ends up with twenty-five.

Without structure you get: hard-coded paths that break on anyone else's machine, the same cleaning logic copy-pasted into six queries that then drift apart, a flat list where staging queries and output tables are indistinguishable, and no way to tell which query feeds which.

The test
Could someone else open your file and, within five minutes, work out which queries load to the model, which are helpers, and where a given transformation happens?

If not, the file is yours alone — and that is a liability the moment you are on leave.
03

Concept

Parameters

Home › Manage Parameters › New Parameter. A named value you reference in queries instead of hard-coding it.

What parameters are worth using for
UseExampleBenefit
File and folder pathsDataFolderThe file works on any machine; a .pbit prompts for it on open
Server and database namesSqlServer, DatabaseSwitch between dev, test and production without editing queries
Date rangesStartDateLimit the data loaded during development, widen it for production
ThresholdsMinOrderValueBusiness rules become visible and adjustable, not buried in a filter step
Incremental refreshRangeStart, RangeEndReserved names required by Power BI — Module 11 Lesson 4

Parameter types

  • Any value — free text or number.
  • List of values — a dropdown, which prevents typos and documents the valid options.
  • Query — the list comes from another query, so it stays current.
Use a list of values wherever the options are known
A parameter offering a free-text server name invites a typo that produces a confusing connection error. A parameter offering a dropdown of DEV / TEST / PROD cannot be got wrong and tells the next person what the valid environments are.

It is the same principle as rule-based filters and Choose Columns: make the correct thing the easy thing.

Reference, duplicate and group

Three organisational tools people confuse
ActionCreatesLinked to the original?
ReferenceA new query starting from the original's outputYes — changes to the original flow through
DuplicateAn independent copy of all the stepsNo — the two diverge from that moment
GroupA folder in the Queries panen/a — purely organisational
Duplicate is almost always the wrong choice
It feels safe — a copy cannot break the original. But you now have two copies of the same cleaning logic. Fix a bug in one and the other still has it. Six months later nobody knows which is authoritative.

Reference is what you usually want. One source of truth; branches that build on it.

The staging pattern

The standard structure for a maintainable file:

  1. Source queries — connect and do minimal shaping. Load disabled.
  2. Staging queries — reference the source, do the cleaning. Load disabled.
  3. Output queries — reference staging, do any final shaping. These load to the model.

Right-click any query → Enable load to toggle whether it becomes a table in the model. Turning it off for helpers keeps the Data pane clean and the model small.

Groups

Right-click in the Queries pane → New Group. A conventional set:

  • 0 Parameters
  • 1 Sources
  • 2 Staging
  • 3 Model — the queries that actually load
  • 4 Data Quality — the check queries from Lesson 3
  • 9 Functions

The numeric prefixes force a sensible order, since groups sort alphabetically.

Reading the Advanced Editor

Home › Advanced Editor shows the M behind your Applied Steps. It has always been there; this is the point at which reading it becomes useful.

Power Query M · The structure of every query you have built
let
    StepName1 = <expression>,
    StepName2 = <expression using StepName1>,
    StepName3 = <expression using StepName2>
in
    StepName3
  • let introduces a list of named steps.
  • Each step is Name = expression, separated by commas — no comma after the last one.
  • in names the step that is the query's result. Usually the last, and it does not have to be.
  • Step names with spaces are written #"Step Name". That is where the hash marks come from.
  • M is case-sensitive. Table.SelectRows works; table.selectrows does not.
You can delete a step by removing its line
Provided you fix the reference in the following step. Editing M directly is often faster than clicking, especially for reordering — but check the step chain afterwards, because a broken reference produces an error naming a step that no longer exists.

Custom functions

A function is a query that takes arguments. Two ways to make one:

1 · From a parameter (the easy route)

  1. Build a query that does the transformation for one case, using a parameter for the thing that varies.
  2. Right-click the query → Create Function.
  3. Power Query generates a function taking that parameter as its argument.

This is exactly what Combine Files does behind the scenes (Module 2 Lesson 2) — the same pattern, made explicit.

2 · By hand

Power Query M · A function to clean any text column consistently
// CleanText.pq  -  one definition of "cleaned text" for the whole file
(input as nullable text) as nullable text =>
let
    Trimmed  = Text.Trim(input ?? ""),
    Cleaned  = Text.Clean(Trimmed),
    Collapsed = Text.Replace(Cleaned, "  ", " "),
    Result   = if Collapsed = "" then null else Collapsed
in
    Result

Invoke it in a Custom Column: CleanText([FullName]). Or across several columns at once:

Power Query M · Apply a function to several columns
Table.TransformColumns(Source, {
    {"FullName", CleanText, type nullable text},
    {"Email",    CleanText, type nullable text},
    {"Country",  CleanText, type nullable text}
})
Why this is worth doing
Without the function, "clean text" is implemented separately in every query that needs it, and the implementations drift. With it, there is one definition. Improve it — add non-breaking-space handling, say — and every column in every query improves at once.

This is the same argument as a database view versus duplicated Power Query logic (Module 2 Lesson 3), one level down.
04

Visual Explanation

A well-organised query file, as it appears in the Queries pane.

Reference · Query structure
📁 0 Parameters
   ⚙ DataFolder            "C:\bwb\data\"        (text)
   ⚙ Environment           PROD                     (list: DEV / TEST / PROD)
   ⚙ StartDate             2024-01-01               (date)

📁 1 Sources                                        [load disabled]
   ▤ src_Sales             raw CSV, headers promoted, nothing else
   ▤ src_Products          raw CSV
   ▤ src_Customers         raw CSV

📁 2 Staging                                        [load disabled]
   ▤ stg_Sales             references src_Sales -> reduce, clean, type
   ▤ stg_Products          references src_Products -> dedupe, clean
   ▤ stg_Customers         references src_Customers -> standardise country
   ▤ map_Country           Enter Data: raw -> standard

📁 3 Model                                          [LOADS TO MODEL]
   ▦ Sales                 references stg_Sales
   ▦ Product               references stg_Products
   ▦ Customer              references stg_Customers
   ▦ Date                  generated calendar

📁 4 Data Quality                                   [LOADS TO MODEL]
   ▦ DQ_Issues             unmapped values, duplicate keys, bad conversions

📁 9 Functions
   ƒ CleanText             (text) => text
   ƒ StandardiseCountry    (text) => text
What this structure buys you
One place to change a path. The parameter.
One place to change cleaning. The staging query.
Obvious what loads. Only groups 3 and 4.
Obvious what feeds what. The naming prefix plus Query Dependencies.
One definition of shared logic. The functions group.

Query Dependencies

View › Query Dependencies draws a diagram of which query feeds which. On a file like the one above it is immediately readable; on a flat unorganised file it is the only way to work out what is going on.

05

Power BI Demonstration

Create a path parameter
Home › Manage Parameters › New Parameter. Name DataFolder, type Text, current value your data folder path including the trailing backslash.
Use it in a query
Open a source query in the Advanced Editor. Change the literal path to DataFolder & "bwb-retail-sales.csv".
Create a list parameter
New Parameter, name Environment, Suggested Values: List of values, enter DEV, TEST, PROD. Note that it now presents as a dropdown.
Reference versus duplicate
Right-click a query → Reference. Then right-click again → Duplicate. Add a step to the original and watch which of the two changes.
Disable load on helpers
Right-click each source and staging query → untick Enable load. Their names go italic in the pane.
Create the groups
Right-click in the Queries pane → New Group. Create the six groups from section 04 and drag queries into them.
Open Query Dependencies
View › Query Dependencies. Confirm the diagram matches the structure you intended. If it looks tangled, the structure is tangled.
Read a query in the Advanced Editor
Home › Advanced Editor on your most complex query. Identify the let, the steps, and the in. Find a step name with spaces and note the #"…" syntax.
Edit M directly
Reorder two steps by cutting and pasting the lines, then fix the references so each step points at the right predecessor. Much faster than dragging for a multi-step reorder.
Write a function by hand
Right-click in the Queries pane → New Query › Blank Query. Open the Advanced Editor and paste the CleanText function from section 03. Name it CleanText.
Invoke it
In a staging query, add a step: Table.TransformColumns(PreviousStep, {{"FullName", CleanText, type nullable text}}).
Create a function from a parameter
Build a query that filters a table by a parameter value, then right-click → Create Function. Observe what Power Query generates.
HomeManage ParametersNew Parameter
06

Example Dataset

Any of the course files will do for this lesson — the subject is structure rather than content. Using several at once makes the organisational benefit more obvious.

bwb-retail-sales.csv
1.3 MB · 15067 rows · 13 columns · CSV
Business context
Two full years (Jan 2024 – Dec 2025) of order-line transactions for a mid-sized multi-channel retailer selling across six product categories, five regions and three channels. Contains real seasonality — a November peak, a February trough — and category margins that differ enough to make the analysis interesting. This is the course's primary fact table.

Columns & data types

ColumnData typeMeaning
OrderIDTextOrder identifier. One order can have several rows — one per product line.
OrderDateDateDate the order was placed. Use this for time intelligence.
ShipDateDateDate the order shipped. A second date column, for the role-playing dimension lesson.
CustomerKeyTextForeign key to bwb-customers.csv.
ProductKeyTextForeign key to bwb-products.csv.
StoreKeyTextForeign key to bwb-stores.csv. Blank for non-store channels.
ChannelTextOnline, Retail Store or Partner.
QuantityWhole numberUnits sold on this line.
UnitPriceDecimalList price per unit before discount.
DiscountDecimalDiscount rate applied, 0 to 0.30.
NetSalesDecimalRevenue after discount. The measure you will sum most often.
COGSDecimalCost of goods sold for this line.
ProfitDecimalNetSales minus COGS. Pre-computed so you can validate your own DAX.

Questions this dataset can answer

  • Which category generates the most revenue, and is it also the most profitable?
  • How did monthly revenue trend across the two years, and where is the seasonal peak?
  • Which region has the highest revenue per customer?
  • What share of revenue comes from discounted orders, and does discounting improve profit?
  • Which products account for the top 20% of revenue?
07

Step-by-Step Exercise

Restructure a real file
Take the queries you have built across this module and reorganise them into the six-group staging pattern.
Parameterise every path
One DataFolder parameter, referenced by every source query. Test it by moving your data folder and changing only the parameter.
Convert duplicates to references
If you have any duplicated queries, replace them with references so there is one source of truth.
Disable load on everything except model and DQ queries
Then check the Data pane in Power BI — it should show only the tables you intend.
Write two functions
CleanText from section 03, and one of your own — perhaps a StandardiseCountry that wraps the mapping merge from Lesson 3.
Apply the function across three columns in one step
Using Table.TransformColumns.
Draw your own dependency diagram before opening the real one
Sketch which query feeds which from memory. Then open View › Query Dependencies and compare. Any surprise is a sign the structure is not as clear as you thought.
Export a .pbit and test it
Export a template, open it, and confirm it prompts for DataFolder. This is the payoff for parameterising.
08

Expected Result

What done looks like
CheckExpected
Queries paneSix numbered groups, nothing loose at the top level
Italic query namesAll sources and staging — load disabled
Data pane in Power BIOnly the model tables and DQ_Issues
Hard-coded pathsNone — all via DataFolder
Duplicated queriesNone — references only
Query Dependencies diagramA readable left-to-right flow, no crossing tangles
.pbit on openPrompts for DataFolder before refreshing
The dependency-diagram exercise is the real test
If your sketch matched the actual diagram, your file's structure matches your mental model of it — which means someone else can follow it too.

If it did not match, that gap is the problem. A file you cannot draw from memory is a file you will get lost in, and so will everyone after you.
09

Common Mistakes

Duplicating a query instead of referencing it
Two independent copies of the same logic that drift apart. A fix applied to one leaves the other broken, and nobody knows which is authoritative.

Fix: Use Reference. Duplicate only when you genuinely want an independent copy that will evolve differently — which is rare.
Leaving helper queries enabled for load
Staging and source queries appear as tables in the model, cluttering the Data pane and adding to model size for no benefit.

Fix: Right-click → untick Enable load on everything except your model tables and check queries.
Hard-coding paths, servers or thresholds
The file works only on your machine, and a business rule buried in a filter step is invisible to anyone reviewing it.

Fix: Parameterise. Paths and servers always; thresholds whenever the value is a business decision rather than a technical one.
Never opening the Advanced Editor
You are limited to what the interface exposes, cannot reorder steps efficiently, and cannot read what a query actually does without clicking through every step.

Fix: Open it on a query you built yourself and read it. The structure is simpler than it looks, and it is the same every time.
Copy-pasting the same transformation into several queries
Six implementations of "clean text" that all differ slightly. Improving one improves nothing else.

Fix: Write a function. One definition, invoked everywhere, improved in one place.
10

Professional Tip

Prefix query names so the pane sorts itself
src_ for sources, stg_ for staging, map_ for mapping tables, DQ_ for checks, and plain names for the tables that load to the model.

Combined with numbered groups this means the Queries pane is self-explanatory without anyone having to open anything. It also makes the Query Dependencies diagram readable, because the prefix tells you which layer each box belongs to.

Model tables get plain names deliberately — those are the ones that appear in the Data pane, and stg_Sales would look wrong there.

Two more:

  • Put a description on every query. Right-click → Properties → Description. It shows as a tooltip in the pane and is the only documentation anyone will actually read.
  • Keep source queries genuinely minimal. Connect and promote headers, nothing more. When a source changes shape, having exactly one thin query to fix is worth a great deal.
11

Knowledge Check

Knowledge Check

5 questions. Answer each one to see why the right answer is right.

What is the difference between Reference and Duplicate?

In M, what do let and in do?

Why disable load on staging queries?

What is the main argument for writing a custom function rather than repeating a transformation?

You want a parameter for the target environment. What type should it be?

12

Practical Challenge

Practical Challenge

Restructure a messy query file for handover

You inherit a .pbix with 18 queries in a flat list. Paths are hard-coded, three queries are near-identical duplicates, everything loads to the model, and nobody can tell what feeds what. Restructure it so a new analyst could pick it up, and document what you changed.

Requirements
  • Apply the six-group staging structure
  • Parameterise every path and any hard-coded business threshold you find
  • Replace the three duplicates with references or a function — and justify which you chose
  • Disable load on everything that is not a model table or a DQ check
  • Adopt a naming convention and apply it throughout
  • Produce a handover note explaining the structure and what changed
  • State one thing you deliberately did not change, and why
Reference · The inherited flat query list
Sales2024              CSV from C:\Users\jane\Desktop\data\           [loads]
Sales2025              CSV from C:\Users\jane\Desktop\data\           [loads]
Sales2026              CSV from C:\Users\jane\Desktop\data\           [loads]
Products               CSV, deduped, trimmed                             [loads]
Customers              CSV, country mapping merged inline                [loads]
CountryMap             Enter Data                                        [loads]
Query1                 filters Sales2024 to NetSales > 100               [loads]
Query1 (2)             filters Sales2025 to NetSales > 100               [loads]
Query1 (3)             filters Sales2026 to NetSales > 100               [loads]
Calendar               generated                                         [loads]
Sheet1                 leftover, unused                                  [loads]
... 7 more                                                               [all load]

The three Query1 variants are the interesting problem. They do the same thing to three different tables.

Two possible answers: append the three sales queries first and filter once, or write a function taking a table and returning the filtered version. Which is better depends on whether the three sales files should be one table in the model — and they almost certainly should.

Also notice the year-per-file pattern. Is three queries even the right shape?

13

Solution

Try the challenge properly before opening this. Reading a solution you have not attempted feels like learning, but it is not.

The restructure

1 · The biggest change: the sales files should not be three queries

Replace three queries with the Folder connector
Sales2024, Sales2025 and Sales2026 are the same data in three files. Three queries means three places to fix anything, and a fourth query has to be written by hand every January.

Use the Folder connector (Module 2 Lesson 2) pointed at the data folder, filtered to sales*.csv. One query, and 2027 appears automatically.

This also dissolves the Query1 problem entirely — there is now one table to filter, so the duplicated filter logic has nothing to duplicate.

2 · The resulting structure

Reference · Restructured
📁 0 Parameters
   ⚙ DataFolder          text     "\\fileserver\finance\data\"
   ⚙ MinOrderValue       decimal  100        <- was hard-coded in Query1 x3

📁 1 Sources                                              [load disabled]
   ▤ src_SalesFolder     Folder connector, filtered to sales*.csv
   ▤ src_Products        CSV via DataFolder
   ▤ src_Customers       CSV via DataFolder

📁 2 Staging                                              [load disabled]
   ▤ stg_Sales           references src_SalesFolder -> combine, reduce, clean, type
   ▤ stg_Products        references src_Products    -> dedupe, clean
   ▤ stg_Customers       references src_Customers   -> merge map_Country
   ▤ map_Country         Enter Data: raw -> standard

📁 3 Model                                                [LOADS]
   ▦ Sales               references stg_Sales, filtered to >= MinOrderValue
   ▦ Product             references stg_Products
   ▦ Customer            references stg_Customers
   ▦ Date                generated calendar

📁 4 Data Quality                                         [LOADS]
   ▦ DQ_Issues           unmapped countries, duplicate keys, orphaned product keys

📁 9 Functions
   ƒ CleanText           (nullable text) => nullable text

3 · References or a function for the duplicates?

Neither — the duplication was a symptom
The obvious answers are "reference them" or "write a filter function". Both would work and both would be treating the symptom.

The three Query1 queries existed only because the sales data was in three queries. Consolidating the source removes the need for any of them: one stg_Sales, one filter step in Sales, referencing MinOrderValue.

The general lesson: when you find duplicated logic, ask why it exists before deciding how to share it. Sometimes the duplication is telling you the structure above it is wrong.

4 · The handover note

What I would write
Structure. Queries are grouped by layer. 0 Parameters holds the two values you may need to change. 1 Sources connects to files and does nothing else. 2 Staging holds all the cleaning logic — this is where you go to change how data is treated. 3 Model is what loads into Power BI. 4 Data Quality returns rows only when something is wrong; its count is on the hidden _Validation page.

To move the data folder: change the DataFolder parameter. Nothing else.

To change the minimum order value: change MinOrderValue. It was previously hard-coded in three places, which is why the reports had disagreed.

New sales files: drop them in the folder named sales*.csv. They are picked up automatically — no query change needed.

What changed: three sales queries became one Folder connection; three duplicate filter queries were removed; an unused Sheet1 query was deleted; load was disabled on 7 helper queries that were appearing as tables in the model; all paths were parameterised; a DQ check query was added.

5 · What I deliberately did not change

The country mapping stays as Enter Data
map_Country is a hard-coded table inside the file. The better design is an Excel file on SharePoint that a business user can maintain without opening Power Query, or a database reference table.

I left it alone deliberately, for two reasons.

First, it is not currently broken. Changing it introduces a new external dependency and a new failure mode, in a handover where the priority is that the new analyst can understand and run what exists.

Second, and more importantly, it is not my decision to make. Moving the mapping out of the file means someone has to own it, maintain it, and be told when to update it. That is an organisational commitment, not a technical change. I have flagged it as a recommendation with the reasoning, and left the decision to whoever will own the process.

Restructuring work has a strong pull toward changing everything you can see. Knowing where to stop — and being explicit about what you left and why — is part of doing it well.

The three judgements this challenge tests

Recognising that duplication was a symptom. The obvious fix shares the logic; the right fix removes the need for it. Asking "why does this duplication exist?" before "how do I share it?" is the habit worth building.

Writing the handover note for the reader, not the author. It leads with "to change X, do Y" rather than with a list of what was refactored. The new analyst needs to operate the thing before they need its history.

Deliberately not fixing something. The mapping table is a genuine weakness and changing it would have been defensible. Leaving it, flagging it, and explaining that the decision belongs to someone else demonstrates a sense of scope that matters more in real work than technical thoroughness does.

14

Lesson Summary

Key takeaways
  • Parameters make queries portable and business rules visible. Use a list of values wherever the options are known.
  • Reference, do not duplicate. References stay linked; duplicates drift apart.
  • Use the staging pattern: Parameters → Sources → Staging → Model → Data Quality → Functions, in numbered groups.
  • Disable load on everything that is not a model table or a check query.
  • Every M query is let steps in result. Step names with spaces use #"…". M is case-sensitive.
  • Custom functions give shared logic one definition, so improvements apply everywhere at once.
  • View › Query Dependencies shows what feeds what. If you cannot sketch it from memory, the structure is not clear enough.
  • When you find duplicated logic, ask why it exists before deciding how to share it.

Syllabus topics delivered

ParametersQuery dependenciesAdvanced EditorIntroduction to MBuilding reusable transformationsCustom functions
15

Progress

Marking a lesson complete stores your progress in this browser and updates your BitWithBite learner dashboard. Nothing is uploaded anywhere.

Lesson complete — 32% of Power BI Mastery done
Progress saved. That completes Module 3 — Module 4 (Data Modeling) is still being written. The roadmap shows what is coming next.