概述
Microsoft Power Query 提供了一种功能强大的“获取数据”体验,其中包含许多功能。 Power Query 的核心功能是筛选和合并,即从一个或多个受支持的数据源集合中“混合”数据。 任何此类数据混合都使用 Power Query 公式语言(非正式地称为“M”)表示。 Power Query 在各种Microsoft产品(包括 Excel、Power BI、Analysis Services 和 Dataverse)中嵌入 M 文档,以实现可重复的数据混合。
本文档提供 M 的规范。在简要介绍旨在构建一些第一个直觉和熟悉语言之后,文档将准确介绍语言,具体步骤如下:
词法结构定义词法有效的文本集。
值、表达式、环境和变量、标识符和计算模型构成了语言 的基本概念。
基元和结构化 值的详细规范定义了语言的目标域。
值具有 类型,本身是一种特殊的值类型,既具有基本值的特征,又具有特定于结构化值形状的其他元数据。
M 中的 一组运算符 定义可以形成哪种类型的表达式。
函数是另一种特殊值,为 M 的丰富标准库提供基础,并允许添加新抽象。
在表达式计算期间应用运算符或函数时,可能会出现错误。 虽然错误不是值,但有一些方法可以 处理将错误 映射回值的错误。
允许表达式 引入用于在较小步骤中构建复杂表达式的辅助定义。
如果表达式 支持条件计算。
各节 提供简单的模块化机制。 (Power Query 尚未利用节。
最后, 合并语法 将本文档所有其他部分的语法片段收集到单个完整定义中。
对于计算机语言理论家:本文档中指定的公式语言是一种最纯、更高级别、动态类型化、部分延迟的功能语言。
表达式和值
M 中的中心构造是 表达式。 可以计算表达式(计算),从而生成单个 值。
虽然许多值可以字面写为表达式,但值不是表达式。 例如,表达式 1 的计算结果为值 1;表达式 1+1 的计算结果为值 2。 这种区别是微妙的,但很重要。 表达式是用于计算的配方;值是计算结果。
以下示例说明了 M 中可用的不同类型的值。作为一种约定,使用文本形式编写一个值,在表达式中,它们将显示在计算结果为仅该值的表达式中。 (请注意, // 指示继续到行尾的注释的开始。
基元值是单部分值,例如数字、逻辑、文本或 null。 NULL 值可用于指示缺少任何数据。
123 // A number true // A logical "abc" // A text null // null value列表值是一个有序的值序列。 M 支持无限列表,但如果以文本的形式写入,则列表的长度固定。 大括号字符
{,并}表示列表的开头和结尾。{123, true, "A"} // list containing a number, a logical, and // a text {1, 2, 3} // list of three numbers记录是一组字段。 字段是名称/值对,其中名称是字段记录中唯一的文本值。 记录值的文字语法允许在没有引号的情况下编写名称,该窗体也称为 标识符。 下面显示了一条记录,其中包含名为“”、“
A”B和“”C的三个字段,这些字段具有值1,2以及3。[ A = 1, B = 2, C = 3 ]表是一组按列(按名称标识)和行的值。 没有用于创建表的文本语法,但有几个标准函数可用于从列表或记录创建表。
例如:
#table( {"A", "B"}, { {1, 2}, {3, 4} } )这会创建以下形状的表格:
函数是一个值,使用参数调用时,会生成一个新值。 函数通过列出 函数的参数在 括号中编写,后跟去符号
=>,后跟定义函数的表达式。 该表达式通常引用参数(按名称)。(x, y) => (x + y) / 2`
Evaluation
M 语言的计算模型是在电子表格中通常找到的评估模型之后建模的,可以在其中根据单元格中的公式之间的依赖关系来确定计算顺序。
如果在电子表格(如 Excel)中编写公式,则计算结果右侧的值可能会识别公式:
在 M 中,表达式的各个部分可以按名称引用表达式的其他部分,计算过程会自动确定所引用表达式的计算顺序。
可以使用记录生成与上一个电子表格示例等效的表达式。 初始化字段的值时,可以使用字段的名称引用记录中的其他字段,如以下示例所示:
[
A1 = A2 * 2,
A2 = A3 + 1,
A3 = 1
]
前面的表达式等效于以下示例(在这两个表达式的计算结果都等于值):
[
A1 = 4,
A2 = 2,
A3 = 1
]
记录可以包含在其他记录中,也可以 嵌套。 可以使用 查找运算符 ([]) 按名称访问记录的字段。 例如,以下记录具有一个名为Sales包含记录的字段,以及一个名为Total访问FirstHalf记录的Sales字段和SecondHalf字段:
[
Sales = [ FirstHalf = 1000, SecondHalf = 1100 ],
Total = Sales[FirstHalf] + Sales[SecondHalf]
]
计算前一个表达式时,其等效于以下示例:
[
Sales = [ FirstHalf = 1000, SecondHalf = 1100 ],
Total = 2100
]
记录也可以包含在列表中。 可以使用 位置索引运算符 ({}) 通过其数值索引访问列表中的项。 从列表开头使用从零开始的索引引用列表中的值。 例如,索引 0 和 1 用于引用以下列表中的第一项和第二项:
[
Sales =
{
[
Year = 2007,
FirstHalf = 1000,
SecondHalf = 1100,
Total = FirstHalf + SecondHalf // 2100
],
[
Year = 2008,
FirstHalf = 1200,
SecondHalf = 1300,
Total = FirstHalf + SecondHalf // 2500
]
},
TotalSales = Sales{0}[Total] + Sales{1}[Total] // 4600
]
列表和记录成员表达式(以及 让表达式)是使用 惰性计算的,这意味着它们仅根据需要进行计算。 所有其他表达式都使用 预先计算进行计算,这意味着在计算过程中遇到这些表达式时会立即计算这些表达式。 考虑这一点的一个好方法是记住,计算列表或记录表达式会返回一个列表或记录值,该值本身记得在请求时(通过查找或索引运算符)计算其列表项或记录字段的方式。
Functions
在 M 中, 函数 是从一组输入值映射到单个输出值。 函数通过首先命名所需的输入值集(函数的参数),然后提供一个表达式,该表达式使用这些输入值(函数正文)在转到(=>)符号之后计算函数的结果。 例如:
(x) => x + 1 // function that adds one to a value
(x, y) => x + y // function that adds two values
函数是一个类似于数字或文本值的值。 以下示例显示了一个函数,该函数是添加字段的值,然后从其他几个字段 调用或执行。 调用函数时,将指定一组值,这些值在逻辑上替换为函数主体表达式中所需的输入值集。
[
Add = (x, y) => x + y,
OnePlusOne = Add(1, 1), // 2
OnePlusTwo = Add(1, 2) // 3
]
图书馆
M 包括一组常见的定义,可用于从名为 标准库的表达式使用,或只是一组简短的 库 。 这些定义由一组命名值组成。 库提供的值的名称可用于表达式中,而无需由表达式显式定义。 例如:
Number.E // Euler's number e (2.7182...)
Text.PositionOf("Hello", "ll") // 2
运营商
M 包括一组可用于表达式的运算符。
运算符 应用于 作数 以形成符号表达式。 例如,在表达式 1 + 2 中,数字 1 和 2 作数为作数,运算符是加法运算符 (+)。
运算符的含义可能因作数的类型而异。 例如,除数字外,加运算符还可用于其他类型的值:
1 + 2 // numeric addition: 3
#time(12,23,0) + #duration(0,0,2,0)
// time arithmetic: #time(12,25,0)
具有作数依赖含义的运算符的另一个示例是组合运算符(&):
"A" & "BC" // text concatenation: "ABC"
{1} & {2, 3} // list concatenation: {1, 2, 3}
[ a = 1 ] & [ b = 2 ] // record merge: [ a = 1, b = 2 ]
请注意,某些运算符不支持所有值组合。 例如:
1 + "2" // error: adding number and text isn't supported
计算结果时遇到未定义的运算符条件的表达式计算结果为 错误。
Metadata
元数据 是与值关联的值的相关信息。 元数据表示为记录值,称为 元数据记录。 元数据记录的字段可用于存储值的元数据。
每个值都有一个元数据记录。 如果未指定元数据记录的值,则元数据记录为空(没有字段)。
元数据记录提供了一种方法,以不显眼的方式将其他信息与任何类型的值相关联。 将元数据记录与值关联不会更改值或其行为。
元数据记录值y使用语法x meta y与现有值x相关联。 例如,以下代码将元数据记录与Rating文本值"Mozart"相关联和Tags字段:
"Mozart" meta [ Rating = 5, Tags = {"Classical"} ]
对于已携带非空元数据记录的值,应用元的结果是计算现有和新元数据记录的记录合并。 例如,以下两个表达式等效于彼此和上一个表达式:
("Mozart" meta [ Rating = 5 ]) meta [ Tags = {"Classical"} ]
"Mozart" meta ([ Rating = 5 ] & [ Tags = {"Classical"} ])
可以使用函数 Value.Metadata 访问给定值的元数据记录。 在以下示例中 ComposerRating ,字段中的表达式访问字段中值的 Composer 元数据记录,然后访问 Rating 元数据记录的字段。
[
Composer = "Mozart" meta [ Rating = 5, Tags = {"Classical"} ],
ComposerRating = Value.Metadata(Composer)[Rating] // 5
]
Let 表达式
到目前为止显示的许多示例都包括在表达式结果中表达式的所有文本值。 该 let 表达式允许计算一组值、分配的名称,然后在前面后面的 in表达式中使用。 例如,在我们的销售数据示例中,可以执行以下作:
let
Sales2007 =
[
Year = 2007,
FirstHalf = 1000,
SecondHalf = 1100,
Total = FirstHalf + SecondHalf // 2100
],
Sales2008 =
[
Year = 2008,
FirstHalf = 1200,
SecondHalf = 1300,
Total = FirstHalf + SecondHalf // 2500
],
TotalSales = Sales2007[Total] + Sales2008[Total]
in
TotalSales // 4600
上述表达式的结果是从绑定到名称和Sales2007Sales2008的值中计算的数字值(4600)。
If 表达式
表达式 if 根据逻辑条件在两个表达式之间进行选择。 例如:
if 2 > 1 then
2 + 2
else
1 + 1
如果逻辑表达式 (2 + 2) 为 true,则选择第一个表达式 (2 > 1),如果为 false,则选择第二个表达式 (1 + 1)。 将计算所选表达式(在本例 2 + 2中 if ),并成为表达式的结果(4)。
Errors
错误表明计算表达式的过程无法生成值。
错误由遇到错误条件的运算符和函数或使用错误表达式引发。 使用 try 表达式处理错误。 引发错误时,指定了一个值,该值可用于指示错误发生的原因。
let Sales =
[
Revenue = 2000,
Units = 1000,
UnitPrice = if Units = 0 then error "No Units"
else Revenue / Units
],
UnitPrice = try Number.ToText(Sales[UnitPrice]),
Result = "Unit Price: " &
(if UnitPrice[HasError] then UnitPrice[Error][Message]
else UnitPrice[Value])
in
Result
上面的示例访问 Sales[UnitPrice] 字段并设置生成结果的值的格式:
"Unit Price: 2"
Units如果该字段为零,则UnitPrice字段将引发错误,该错误将由该try字段处理。 然后,生成的值为:
"Unit Price: No Units"
try表达式将正确的值和错误转换为记录值,该值指示表达式是否try处理错误,以及处理错误时提取的正确值或错误记录。 例如,请考虑引发错误的以下表达式,然后立即处理它:
try error "negative unit count"
此表达式的计算结果为以下嵌套记录值,说明上一个单价示例中的[HasError]和[Error][Message]字段查找。
[
HasError = true,
Error =
[
Reason = "Expression.Error",
Message = "negative unit count",
Detail = null
]
]
常见情况是将错误替换为默认值。 表达式 try 可与可选 otherwise 子句一起使用,以精简形式实现该表达式:
try error "negative unit count" otherwise 42
// 42