Null Handling
How null values propagate through expressions, and practical patterns for dealing with missing data in M.
null in M represents the absence of a value. It is a first-class value with its own type, and it behaves differently from nulls in SQL, Python, or JavaScript. Understanding how null propagates through expressions is critical for writing correct queries.
Null Propagation
Most operations involving null produce null as the result. This is called null propagation:
1 + null // null
"Hello " & null // null
null > 5 // nullThis differs from languages where null operations throw errors. In M, null silently propagates through arithmetic, text concatenation, and comparisons.
Does null = null?
This is one of M's most important behaviors. The equality operator returns true:
null = null // trueBut relational operators return null:
null > null // null
null >= null // null
null < 5 // nullThis means filtering with a comparison can silently drop rows where the column is null:
// This will NOT include rows where Quantity is null
Table.SelectRows(Sales, each [Quantity] > 0)If [Quantity] is null, the expression null > 0 evaluates to null, which is not true, so the row is excluded.
Testing for Null
Use the = null comparison to check for null values:
if [Value] = null then "Missing" else Text.From([Value])Or use it in filters:
// Keep only rows where Region is not null
Table.SelectRows(Sales, each [Region] <> null)Null Coalescing Patterns
M does not have a dedicated null coalescing operator, but you can use if or the ?? shorthand (available in newer versions of the engine):
// Using if/then/else
let
result = if [Notes] = null then "(no notes)" else [Notes]
in
result
// Using the ?? operator (where available)
// [Notes] ?? "(no notes)"Replacing Nulls in Tables
To replace null values across a column, use Table.ReplaceValue with Replacer.ReplaceValue:
Table.ReplaceValue(
Sales,
null,
0,
Replacer.ReplaceValue,
{"Quantity"}
)This replaces every null in the Quantity column with 0.
Null in Aggregations
Most aggregation functions ignore null values:
List.Sum({1, null, 3}) // 4 (null is skipped)
List.Average({10, null, 20}) // 15 (null is skipped)
List.Count({1, null, 3}) // 3 (null IS counted)Note that List.Count counts all items including null, while List.NonNullCount counts only non-null items.
Null vs. MissingField
Null and missing fields are different concepts in M:
- null — The field exists but has no value
- MissingField — The field does not exist on the record at all
Accessing a missing field raises a MissingField.Error (unless handled). See the MissingField Handling concept page for details.
let
record = [Name = "Widget", Price = null]
in
record[Price] // null (field exists, value is null)
// record[Color] // Error: MissingField.ErrorNull in Type Ascription
When assigning types to columns, use nullable to indicate a column may contain null values:
Table.TransformColumnTypes(Source, {
{"Quantity", type nullable number},
{"Region", type nullable text}
})Without nullable, the type system expects every value in that column to be non-null.
Best Practices
- Always consider null when writing filter conditions. If a column might contain nulls, decide whether those rows should be included or excluded.
- Replace nulls early in your transformation pipeline if downstream steps cannot handle them.
- Use
nullabletypes on columns that may legitimately have missing values. - Prefer
List.NonNullCountoverList.Countwhen you need to count actual data values. - Test with null data to ensure your transformations handle edge cases correctly.