It’s surprisingly easy to stop query folding happening in Power Query by changing the data type of a column. This is mentioned in the docs here, and it’s something several people have blogged about already (for example here). However there is something new to note: an option that will allow you to convert text columns to number or date columns in a foldable way for SQL Server data sources.
Consider the following table in a SQL Server database that consists of a single nvarchar(50) column containing numeric values:
Here’s an M query that converts this column into a numeric column and which folds:
let
Source = Sql.Databases(
"localhost",
[UnsafeTypeConversions = true]
),
FoldingTest1 = Source
{[Name = "FoldingTest"]}
[Data],
dbo_NumberFoldingTest = FoldingTest1
{
[
Schema = "dbo",
Item = "NumberFoldingTest"
]
}
[Data],
#"Added Custom" = Table.AddColumn(
dbo_NumberFoldingTest,
"ConvertedNumber",
each Number.From([NumberAsText]),
Int64.Type
)
in
#"Added Custom"
Here’s the output of the query, where a new custom column called ConvertedNumber contains the converted numeric values:
Here’s the resulting SQL generated by Power Query:
select [_].[NumberAsText] as [NumberAsText],
convert(float, [_].[NumberAsText]) as [ConvertedNumber]
from [dbo].[NumberFoldingTest] as [_]
There are three important things to point out about the M query above:
- I have set the (relatively new) UnsafeTypeConversions property on the Sql.Databases function to true
- In the custom column I have used the Number.From function to convert the text in the NumberAsText column to numbers
- I have used the optional third parameter of Table.AddColumn to set the data type of the new custom column to the Int64 type
All these three things are necessary to get a properly typed numeric column in your Power Query query – if you vary from this too much then folding won’t happen.
It’s also possible to use this technique to convert text to datetime values. Here’s another SQL Server table, this time with dates stored in an nvarchar(50) column:
Here’s another M query that does the conversion and folds:
let
Source = Sql.Databases(
"localhost",
[UnsafeTypeConversions = true]
),
FoldingTest = Source
{[Name = "FoldingTest"]}
[Data],
dbo_DateFoldingTest = FoldingTest
{
[
Schema = "dbo",
Item = "DateFoldingTest"
]
}
[Data],
#"Added Custom" = Table.AddColumn(
dbo_DateFoldingTest,
"ConvertedDate",
each DateTime.From([DateAsText]),
type datetime
)
in
#"Added Custom"
And here’s the resulting SQL:
select [_].[DateAsText] as [DateAsText],
convert(datetime2, [_].[DateAsText]) as [ConvertedDate]
from [dbo].[DateFoldingTest] as [_]
Why, you ask, is this new property on Sql.Databases called “UnsafeTypeConversions”? As the name suggests, it allows you to do something that is potentially unsafe. Consider this SQL Server table that has an nvarchar(50) column containing some numeric values and one non-numeric value:
If you connect to this table and set the data type on this column to be Whole Number using the dropdown in the column header (they normal way to change the data type of a column), something like the M code below will be generated:
let
Source = Sql.Databases("localhost"),
FoldingTest = Source
{[Name = "FoldingTest"]}
[Data],
dbo_NumberFoldingErrorsTest
= FoldingTest
{
[
Schema = "dbo",
Item = "NumberFoldingErrorsTest"
]
}
[Data],
#"Changed Type"
= Table.TransformColumnTypes(
dbo_NumberFoldingErrorsTest,
{{"MixedTextNumbers", Int64.Type}}
)
in
#"Changed Type"
Here’s the output of this query:
Note how this query returns four rows and the third row contains the error value shown.
If, however, you try to use the UnsafeTypeConversions approach here using something like the following M:
let
Source = Sql.Databases(
"localhost",
[UnsafeTypeConversions = true]
),
FoldingTest = Source
{[Name = "FoldingTest"]}
[Data],
dbo_NumberFoldingErrorsTest
= FoldingTest
{
[
Schema = "dbo",
Item = "NumberFoldingErrorsTest"
]
}
[Data],
#"Added Custom" = Table.AddColumn(
dbo_NumberFoldingErrorsTest,
"ConvertedToNumber",
each Number.From([MixedTextNumbers]),
Int64.Type
)
in
#"Added Custom"
You get the following result:
Notice now that there is an error value in both columns and, more importantly, only three rows are returned – the fourth has been lost. So, if you are going to use the UnsafeTypeConversions you need to be 100% sure that it will work and that you don’t have problems with your data quality.
[Thanks to Curt Hagenlocher for the information in this post]