M includes values and types as distinct concepts. The type of a value can be inspected using Value.Type(42) A value can be converted with functions such as 3.1 Null is not the same as an empty string or zeroThese values have different meanings:
A test for null uses equality or inequality operators: [Email] = null [Email] <> null For robust cleansing, it is often necessary to handle all three conditions separately. 3.2 Type declarationsTable schemas can be described with type table [CustomerID = Int64.Type, Name = text, Revenue = type number] In practice, Table.TransformColumnTypes(
Source,
{{"CustomerID", Int64.Type}, {"Revenue", type number}, {"OrderDate", type date}}
)Correct types matter because they affect sorting, aggregation, relationships, date intelligence and error detection in the Power BI model. 4. OperatorsOperators combine or compare values. 4.1 Arithmetic operatorsM supports let
Quantity = 4,
UnitPrice = 25,
Revenue = Quantity * UnitPrice,
Tax = Revenue * 0.20,
Total = Revenue + Tax
in
TotalDivision by zero produces an error. A defensive expression can test the denominator before dividing: if [Units] = 0 then null else [Revenue] / [Units] 4.2 Comparison operatorsThe comparison operators are:
Comparisons should normally involve compatible types. Comparing a number with text can produce an error or an unexpected result depending on context. 4.3 Logical operatorsThe principal logical operators are [Status] = "Open" and [Balance] > 0 [Region] = "East" or [Region] = "West" not [IsCancelled] Parentheses clarify complex conditions: ([Status] = "Open" or [Status] = "Pending") and [Balance] > 0 4.4 Concatenation and access operatorsText can be joined with [FirstName] & " " & [LastName] Record fields can be accessed with a field selector: Customer[Name] A table column can be extracted with: Sales[Amount] A list item is accessed by a zero-based index: Values{0}A table row can also be accessed by index, returning a record: Sales{0}[Amount]An invalid field name or index produces an error. The 5. ListsA list is an ordered sequence enclosed in braces: {10, 20, 30, 40}Lists may contain different types, although homogeneous lists are easier to process reliably: {"North", "South", "East", "West"}Important list functions include: List.Count({10, 20, 30})
List.Sum({10, 20, 30})
List.Max({10, 20, 30})
List.First({10, 20, 30})
List.Last({10, 20, 30})
List.Distinct({"A", "B", "A"})
List.Transform({1, 2, 3}, each _ * 10)The underscore
List.Select({1, 2, 3, 4, 5}, each _ > 3)This returns Lists are useful for parameter values, lookup collections, dynamic column names and intermediate calculations. However, converting a very large table to a list can consume substantial memory and may reduce query performance. 6. RecordsA record is a collection of named fields enclosed in square brackets: [Product = "Laptop", Quantity = 3, UnitPrice = 850] Fields are accessed by name: Order[Quantity] Records can be created or modified using functions such as Record.AddField([Name = "Amina"], "Department", "Finance") The Table.AddColumn(Source, "GrossAmount", each [Quantity] * [UnitPrice], type number) The expression Useful record functions include: Record.FieldNames(MyRecord) Record.Field(MyRecord, "Region") Record.HasFields(MyRecord, "Region") Record.ToTable(MyRecord) 7. TablesA table is a structured value made up of rows and columns. Each row can be understood as a record, while a column can be understood as a list. This relationship explains why M provides both A small table can be created with #table(
{"Product", "Quantity", "Price"},
{{"A", 2, 10}, {"B", 3, 15}}
)Common table operations include: Table.SelectRows(Source, each [Quantity] > 0)
Table.SelectColumns(Source, {"Product", "Quantity"})
Table.RemoveColumns(Source, {"UnwantedColumn"})
Table.RenameColumns(Source, {{"Qty", "Quantity"}})
Table.Sort(Source, {{"Quantity", Order.Descending}})Adding a calculated column: Table.AddColumn(
Source,
"LineValue",
each [Quantity] * [Price],
type number
)Grouping data: Table.Group(
Source,
{"Region"},
{{"TotalSales", each List.Sum([Sales]), type number}}
)The expression Joining tables is commonly performed with Table.NestedJoin(
Sales, {"ProductID"},
Products, {"ProductID"},
"ProductDetails", JoinKind.LeftOuter
)The join creates a nested table column. 8. Functions and the |
| Namespace | Typical use | Examples |
|---|---|---|
Text | Text cleansing and analysis | Text.Trim, Text.Upper, Text.Contains |
Number | Numeric operations | Number.Round, Number.Abs, Number.From |
Date | Date operations | Date.Year, Date.Month, Date.StartOfMonth |
DateTime | Date-time operations | DateTime.Date, DateTime.LocalNow |
List | Ordered collections | List.Transform, List.Select, List.Sum |
Record | Named fields | Record.Field, Record.HasFields |
Table | Tabular transformations | Table.SelectRows, Table.Group, Table.Join |
Value | Value and type operations | Value.Type, Value.ReplaceType |
Table.Buffer | Controlled buffering | Table.Buffer |
Examples of text standard-library functions:
Text.Trim(" North ")
Text.Upper("uk")
Text.Replace("AB-123", "-", "")
Text.Contains("Power BI", "BI")Culture can affect conversion and formatting. For example, decimal separators and date conventions vary by region. When importing files or converting text, explicitly select an appropriate locale where the interface or function supports it. Otherwise, a value such as 01/02/2025 may be interpreted differently in different environments.
The Advanced Editor displays the complete M expression for a query. It is opened from the Power Query Editor through the relevant query-management command. The exact menu location can vary by Power BI version, but the purpose is consistent: view and edit the query as code.
A disciplined editing workflow is:
in expression returns the intended step.A typical manually formatted query is:
let
Source = Excel.CurrentWorkbook(){[Name = "Sales"]}[Content],
CleanNames = Table.TransformColumnNames(Source, Text.Trim),
Typed = Table.TransformColumnTypes(
CleanNames,
{
{"OrderDate", type date},
{"Quantity", Int64.Type},
{"UnitPrice", type number}
}
),
AddedValue = Table.AddColumn(
Typed,
"LineValue",
each [Quantity] * [UnitPrice],
type number
),
Filtered = Table.SelectRows(AddedValue, each [LineValue] > 0)
in
FilteredThe graphical editor may generate step names with spaces and escaped identifiers. This is valid M. Manual editing should preserve the exact names or update every reference consistently.
Typical causes include:
let steps.in expression.For example, this is invalid because the comma after A = 10 is missing:
let
A = 10
B = 20
in
A + BAn expression error occurs when the syntax is valid but the requested operation is invalid. Examples include dividing by zero, referencing a missing field or applying a text function to an incompatible value.
A column containing mixed values may prevent numerical aggregation. Apply an explicit type and inspect conversion errors rather than assuming that all source values are valid.
A query may work until a source system renames or removes a column. Robust solutions can validate required columns using Table.HasColumns before performing transformations:
if Table.HasColumns(Source, {"CustomerID", "Amount"})
then Source
else error "Required columns are missing"Power Query privacy levels help prevent unintended data disclosure when combining sources. A query can fail or behave differently if privacy settings restrict source combination. Privacy levels should be configured according to organizational policy, not simply disabled to make an error disappear.
Common causes include loading unnecessary columns, filtering after expensive operations, repeated source access, row-by-row operations over very large data and premature buffering. Where query folding is supported, filters and projections should generally be pushed toward the source. Table.Buffer should be used cautiously because it can increase memory use and prevent beneficial source folding.
The following is an illustrative case.
A distributor receives monthly sales files from three regional offices. The files have inconsistent column names, occasional blank quantities and product codes containing extra spaces. Management requires a single Power BI table with reliable dates, quantities, revenue and region labels.
The source files have these differences:
Qty, another uses Quantity.A custom function can standardize one file, while a separate query invokes that function for every file in a folder. A simplified transformation function is:
(fileTable as table, regionName as text) as table =>
let
StandardNames = Table.RenameColumns(
fileTable,
{{"Qty", "Quantity"}},
MissingField.Ignore
),
CleanProduct = Table.TransformColumns(
StandardNames,
{{"ProductCode", each Text.Trim(Text.From(_)), type text}}
),
SafeAmount = Table.AddColumn(
CleanProduct,
"AmountNumber",
each try Number.From([Amount]) otherwise null,
type number
),
AddRegion = Table.AddColumn(
SafeAmount,
"Region",
each regionName,
type text
),
AddRevenue = Table.AddColumn(
AddRegion,
"Revenue",
each [Quantity] * [AmountNumber],
type number
)
in
AddRevenueThe function establishes a repeatable contract: it accepts a table and a region name, standardizes the quantity field when necessary, trims product codes, safely converts amounts, adds the region and calculates revenue.
The consolidated query can combine the transformed results using Table.Combine. Invalid amounts become null rather than terminating refresh, but a separate quality-report query should count those null conversions. The resulting model contains consistent fields and can support regional revenue analysis.
MissingField.Ignore is useful when controlled source variation is expected, but it should not hide serious schema failures.try ... otherwise null protects refreshes but must be accompanied by monitoring.M code participates in an organization’s data pipeline and therefore has governance implications. Developers should:
AI-assisted code generation can help produce M expressions, but generated code must be reviewed. The developer remains responsible for syntax, privacy, correctness, source permissions, performance and business interpretation. Sensitive source data should not be pasted into unapproved AI tools.
A reliable M query generally follows these practices:
in expression.Power Query M is a case-sensitive, expression-oriented language in which queries return values. The central let ... in structure assigns names to intermediate expressions and returns a final result. M supports primitive values such as numbers, text, logical values, dates and null, as well as structured values including lists, records, tables and functions.
Operators perform arithmetic, comparison, logical evaluation, concatenation and structured-data access. Lists are ordered collections, records contain named fields and tables combine rows and columns. The each shorthand expresses anonymous functions, while custom functions promote reuse and consistency. Scope controls where names and parameters are available.
The standard library supplies functions for text, numbers, dates, lists, records and tables. Conditional expressions and try support controlled handling of business rules and data-quality problems. The Advanced Editor provides direct access to the complete query and is essential for precise editing and troubleshooting.
Professional M development requires more than code that runs. It requires explicit types, readable structure, awareness of query folding and performance, careful privacy management, documented assumptions and monitoring of rejected or invalid data.
M language: The functional data-transformation language used by Power Query.
Expression: Code that evaluates to a value.
let ... in: The structure used to define named expressions and return a final result.
Value: The result produced by evaluating an expression.
Type: A classification describing the permitted form and behavior of a value.
Null: A value representing the absence of a value.
List: An ordered collection of values.
Record: A collection of named fields.
Table: A structured value containing rows and columns.
Function: A reusable expression that accepts parameters and returns a value.
Scope: The region in which a name or parameter is available.
Identifier: A name used to refer to a step, field or other value.
Quoted identifier: An escaped name written in the form #"...".
each: Shorthand for an anonymous function, commonly used for row transformations.
Query folding: The process of delegating compatible transformations to the data source.
Type conversion: The process of changing a value from one data type to another.
try: An expression used to capture or replace evaluation errors.
Advanced Editor: The interface for viewing and editing a query’s complete M expression.
Privacy level: A setting governing how data sources may be combined.
in clause in a let ... in expression?each keyword represent?let ... in expression and a custom function as examples.try ... otherwise can improve refresh reliability and why it can also create governance risks.Amount column with valid numbers, blank cells and values such as "unknown". Design an M strategy that preserves refresh reliability while allowing the organization to monitor data-quality failures.Table.Buffer. Analyze possible causes and recommend an investigation and optimization plan.What value is returned by the following M expression? let A = 10, B = 5, C = A - B in C
A. 5
B. 10
C. 15
D. 50
Correct Answer: A. 5
Which notation represents a list in M?
A. [A = 1, B = 2]
B. {1, 2, 3}
C. (1, 2, 3)
D. <1, 2, 3>
Correct Answer: B. {1, 2, 3}
What does the in clause specify in a let ... in expression?
A. The data source credential
B. The final value returned by the expression
C. The type of every step
D. The privacy level of the query
Correct Answer: B. The final value returned by the expression
Which expression accesses the first item in a list named Values?
A. Values[0]
B. Values{0}
C. Values(0)
D. Values.0
Correct Answer: B. Values{0}
What is the main purpose of Table.AddColumn?
A. To delete a table
B. To add a calculated or supplied column to a table
C. To convert a table into a record automatically
D. To change a source credential
Correct Answer: B. To add a calculated or supplied column to a table
Which expression safely converts an invalid value to null?
A. Number.From([Amount]) otherwise null
B. try Number.From([Amount]) otherwise null
C. null Number.From([Amount])
D. Number.Try([Amount]) = null
Correct Answer: B. try Number.From([Amount]) otherwise null
In the expression each [Quantity] * [UnitPrice], what does each provide?
A. A database connection
B. An anonymous function applied to the current row
C. A new table schema
D. A text conversion rule
Correct Answer: B. An anonymous function applied to the current row
Which operator is normally used to concatenate text in M?
A. +
B. &
C. ||
D. .
Correct Answer: B. &
Which M value represents the absence of a value?
A. false
B. 0
C. null
D. ""
Correct Answer: C. null
Why should Table.Buffer be used cautiously?
A. It always deletes duplicate rows
B. It can increase memory use and interfere with query folding
C. It changes every column to text
D. It disables all M functions
Correct Answer: B. It can increase memory use and interfere with query folding