运算符
运算符
运算符优先级
运算符优先级控制运算符的计算顺序。括号可以修改默认的优先级。
parenthesized-expression:
( expression )
运算符由高到低:
| 类别 | 表达式 | 说明 |
|---|---|---|
| 主要 | i @i | 标识符表达式 |
| (x) | 括号表达式 | |
| x[i] | 字段访问 | |
| x | 项访问 | |
| x(...) | 函数调用 | |
| {x, y, ...} | 列表初始化 | |
| [i = x, ...] | 记录初始化 | |
| ... | 未实现 | |
| 一元 | +x | 正 |
| -x | 负 | |
notx | 逻辑非 | |
| 元数据 | x metay | 关联元数据 |
| 乘除 | x * y | 乘法 |
| x / y | 除法 | |
| 加减 | x + y | 加法 |
| x - y | 减法 | |
| 关系 | x < y | 小于 |
| x > y | 大于 | |
| x <= y | 小于等于 | |
| x >= y | 大于等于 | |
| 相等 | x = y | 相等 |
| x <> y | 不相等 | |
| 类型断言 | x asy | 返回兼容的值或错误 |
| 类型兼容 | x isy | 返回是否兼容 |
| 逻辑与 | x andy | 短路逻辑与 |
| 逻辑或 | x ory | 短路逻辑或 |
| 合并 | x ??y | null值合并运算符 |
注:截止2024年8月,??还不能用在Power Query SDK中。
运算符和元数据
每个值都有一个关联的记录值,该记录值存储值的其他信息。这个记录被称为值的元数据记录。元数据记录可以与任何值相关联。
元数据记录只是一个常规的记录,该记录和普通的记录没有区别。
元数据记录与值关联是一种非侵入性行为。该操作不会影响值在计算时的行为,除非时显式的检查元数据记录。
元数据记录值通常用于程序和用户需要额外的附加数据时使用。如自定义函数的友好提示。
每个值都有一个元数据记录,默认元数据记录是空记录。
标准库函数Value.Metadata、Value.ReplaceMetadata和Value.RemoveMetadata分别可以查看、替换和清空值的元数据记录。
添加元数据记录。
1 meta [a = 1, b = "ok"]
Value.ReplaceMetadata(1, [b = "ok"])
查看元数据记录。
// []
Value.Metadata("okay")
// [a = 1, b = "ok"]
Value.Metadata(1 meta [a = 1, b = "ok"])
元数据记录不会影响值的计算行为,仅在需要时由程序和用户主动读取。
通常情况下,含有元数据的值在计算后元数据会被清空。
// 6
1 meta [a = 1, b = "ok"] + 5
// []
Value.Metadata(1 meta [a = 1, b = "ok"] + 5)
// []
Value.Metadata(+(1 meta [a = 1, b = "ok"] + 5))
结构递归运算符
值可以循环。
// a = {0, {0, ...}}
let a = {0, @a} in a
// [a = {{ ... }}, b = {{ ... }}]
[a = {b}, b = {a}]
[a = b, b = {a}]
M通过保持列表、记录和表的构造惰性来处理循环值。
M一些运算符由结构递归定义的。例如:记录和列表的相等性分别由记录字段和项列表的联合相等性定义。
对于非循环值,应用结构递归会值的有限拓展:共享嵌套值将被重复遍历,但递归过程总是终止。
应用结构递归时,循环值具有无限拓展。M的语义对这种无限拓展没有特别的适应。例如:尝试比较循环值是否相等,通常会耗尽资源并异常终止。
选择和投影运算符
项访问
根据值从列表或表中从0开始的位置选择数据。
item-access-expression:
item-selection
optional-item-selection
item-selection:
primary-expression { item-selector }
optional-item-selection:
primary-expression { item-selector } ?
item-selector:
expression
对于x{y}:
x是表y是数字,返回表行记录y是记录,返回表中唯一符合记录(条件)的行
x是列表y是数字,返回列表的项值
如果y是负数,将会引发错误。
对于x{y}?,如果无法找到结果,将返回null。
如果x是表、y是记录,但是表中存在多个符合记录的表行时,会产生错误。如果使用?修饰,依然会报错,因为?仅处理找不到结果的情况。
{0, 1, 2, 3}{2} // 2
{0, 1, 2, 3}{4} // error
{0, 1, 2, 3}{4}? // null
#table({"A","B"},{{1,2},{2,2}}){1} // [A = 2, B = 2]
#table({"A","B"},{{1,2},{2,2}}){[A=2]} // [A = 2, B = 2]
#table({"A","B"},{{1,2},{2,2}}){[A=0]} // error
#table({"A","B"},{{1,2},{2,2}}){[A=0]}? // null
#table({"A","B"},{{1,2},{2,2}}){[B=2]} // error
#table({"A","B"},{{1,2},{2,2}}){[B=2]}? // error
项访问不会对列表或表中正在访问的项之外的项进行求值。
{error "0", 1, error "2"}{1} // 1
对于流式处理的列表和表,仅会计算正在访问的项,对于其它的内容可能会被计算,这取决于列表和表的数据源。
表达式x{y}求值时,会传播在计算表达式x或y期间引发的错误。
字段访问
字段访问从记录或表中选择字段值,或将记录或表投影到具有较少字段或列的记录或表。
field-access-expression:
field-selection
implicit-target-field-selection
projection
implicit-target-projection
field-selection:
primary-expression field-selector
field-selector:
required-field-selector
optional-field-selector
required-field-selector:
[ field-name ]
optional-field-selector:
[ field-name ] ?
field-name:
generalized-identifier
quoted-identifier
implicit-target-field-selection:
field-selector
projection:
primary-expression required-projection
primary-expression optional-projection
required-projection:
[ required-selector-list ]
optional-projection:
[ required-selector-list ] ?
required-selector-list:
required-field-selector
required-selector-list , required-field-selector
implicit-target-projection:
required-projection
optional-projection
访问类型
- 字段选择
exp [x]exp [x] ?
- 投影
exp [[x]]exp[[x], [y]]exp[[x], [y]] ?
- 隐式目标(implicit target):从当前环境中的变量
_中提取数据,此时可以省略exp。
x[y]?中的?可以在访问遇到不存在的字段或列时返回null。对于投影,?会将对应的字段或列的所有行填充为null。
[A = 1, B = 2][A] // 1
[A = 1, B = 2][C] // error
[A = 1, B = 2][C]? // null
[A = 1, B = 2][[A], [B]] // [A = 1, B = 2]
[A = 1, B = 2][[A], [C]] // error
[A = 1, B = 2][[A], [C]]? // [A = 1, C = null]
#table({"A","B"},{{1,2},{2,2}})[A] // {1, 2}
#table({"A","B"},{{1,2},{2,2}})[C] // error
#table({"A","B"},{{1,2},{2,2}})[C]? // null
#table({"A","B"},{{1,2},{2,2}})[[A], [C]] // error
#table({"A","B"},{{1,2},{2,2}})[[A], [C]]? // #table({"A","C"}, {{1, null}, {2, null}})
表达式x[y]、x[y]?、x[[y]]、x[[y]]?求值时:
- 传播在计算表达式
x期间引发的错误。 - 计算字段
y时引发的错误与字段y永久关联,然后进行传播。将来对y字段的访问都会引发相同的错误。
附加each说明
each是简化的函数声明,each _ + 5等价于(__) => _+ 5。
当出现each关键字时就隐式声明了形参_。在字段访问时,_可以省略,即_[x]可以简化成[x]。
对于多层each语句,规则同多层环境中的同名变量,使用就近原则取最近一层(算当前层且仅向父层级)的_的值。
元数据运算符
为值添加元数据记录。
metadata-expression:
unary-expression
unary-expression meta unary-expression
1 meta [type = "number"]
let f = 1, g = f meta [type = "number"] in g
对于表达式x meta y:
- 传播在计算表达式
x或y期间引发的错误。 y必须是记录值。
其它详见本文【运算符和元数据】。
备注:
unary-expression:
type-expression
+ unary-expression
- unary-expression
not unary-expression
type-expression:
primary-expression
typeprimary-type
primary-expression:
literal-expression
list-expression
record-expression
identifier-expression
section-access-expression
parenthesized-expression
field-access-expression
item-access-expression
invoke-expression
not-implemented-expression
相等运算符
相等运算符=用于确定两个值是否相等。不相等运算符<>用于确定两个值是否不相等。
equality-expression:
relational-expression
relational-expression=equality-expression
relational-expression<>equality-expression
元数据记录不影响比较结果。
在计算表达式x = y或x <> y时:
- 传播计算表达式
x或y时引发的错误。 - 忽略元数据记录。
- 始终存在
(x = y) = not (x <> y)。 - 两种类型一定不相等。
// 都是false
null = 1
null = "A"
0 = #time(0, 0, 0)
false = 1
1 meta [A = 1] = 1 meta [A = 2]
// 都是true
null = null
1 = 1.0
#nan不等于自身,也是唯一不等于自身的值。
#nan = #nan // false
#nan <> #nan // true
- 非
#nan的数字会使用按位比较。 datetimezone会使用修正后的日期比较。
// false
#datetimezone(1, 1, 1, 12, 0, 0, 0, 0) = #datetimezone(1, 1, 1, 12, 0, 0, 1, 0)
// true
#datetimezone(1, 1, 1, 11, 0, 0, 0, 0) = #datetimezone(1, 1, 1, 12, 0, 0, 1, 0)
#datetimezone(1, 1, 1, 0, 0, 0, -12, 0) = #datetimezone(1, 1, 2, 0, 0, 0, 12, 0)
- 文本会区分大小写。
"a" = "A" // false
- 表达式会求值后再比较
#time(0, 0, 1) = #time(0, 0, 0.999999999999999) // true
- 列表必须项个数相等且相同位置的值相等。
#nan。
{1, 2, 3} = {1, 3, 2} // false
{1, 2, 3} = {1, 2, 3} // true
{1, 2, null} = {1, 2, null} // true
{1, 2, #nan} = {1, 2, #nan} // false
- 记录必须字段名相同且对应的字段值相等,字段顺序可以不同。
#nan。
[A=1, B=2] = [B=2, A=1] // true
[A=1, B=1] = [A=1, C=1] // false
[A=1, B=#nan] = [A=1, C=#nan] // false
- 表必须行数相等、列数相等、列名相同且列的每一行的值相等,列顺序可以不同。
#nan。
// 列顺序可以不同
#table({"A","B"},{{1,2},{3,4}}) = #table({"B","A"},{{2,1},{4,3}})
// 行顺序需要一样
#table({"A","B"},{{3,4},{1,2}}) = #table({"B","A"},{{2,1},{4,3}})
// false
#table({"A","B"},{{1,2},{3,#nan}}) = #table({"A","B"},{{1,2},{3,#nan}})
- 函数值等于自身,不同的函数可能相等也可能不相等。如果两个函数值相等,则调用它们时具有相同的行为。
(() => 1) = (() => 1) // false
- 修改函数类型的函数值与修改前的函数值依然相等。
let
f1 = (x as number) as number => x + 1,
f2 = Value.ReplaceType(f1, type function (y as text) as text)
in
f1 = f2
- 类型值等于自身,不同的类型值可能相等也可能不相等,如果两个类型值相等,则查询一致性时它们拥有相同的行为。
type number = type any // false
type number = Currency.Type // false
type number = Double.Type // false
type number = Value.Type(1) // true
type {number} = Value.Type({1, 2}) // false
type {any} = Value.Type({1, 2}) // true
补充:浮点数误差
// 浮点误差
0.1 + 0.2 = 0.3
关系运算符
关系运算符<、>、<=、>=用于比较值的大小关系。
relational-expression:
additive-expression
additive-expression<relational-expression
additive-expression>relational-expression
additive-expression<=relational-expression
additive-expression>=relational-expression
计算表达式x op y时:
- 传播在计算表达式
x或y时产生的错误。 - 表达式
x和y的计算结果必须是可以比较的标量值,具体为非抽象类型且不是type、list、record的值。
提示
标量值通常理解为传统意义上的单个具体值。
列表、记录、表在M中定义为值,但通常不认为它们是标量值,因为还可以拆分为更多具体的值。
函数等也不能是标里值,因为它们是抽象的指代。
- 操作数除非有
null,否则必须是相同类型。除null以外的不同类型比较会报错。
提示
如果你试图比较不同类型并且仔细观察错误你会发现一个问题:操作符和操作数可能不是实际的内容。
如:"A" >= 1或"A" > 1或"A" < 1或"A" <= 1。
####################错误信息####################
在“”查询中出错。Expression.Error: 无法将运算符 < 应用于类型 Number 和 Text。
详细信息:
Operator=<
Left=1
Right=A
############################################
- 任意个操作数为
null,结果一定是null。 - 非
null的不同类型的值比较,会引发错误。 - 逻辑值
true大于false。 - 文本区分大小写。
"a" > "A"
"56" > "123456"
"123456" > "123"
- 表达式计算后的结果再进行比较。
#duration(0, 0, 1, 0) > #duration(0, 0, 0, 9999) // false
datetimezone会使用修正后的日期比较。
#datetimezone(1, 1, 1, 0, 0, 0, -5, 0) > #datetimezone(1, 1, 1, 0, 0, 0, -4, 0)
- 两个
#nan比较,除了<>以外,任何关系或相等操作符都是false。 #nan不等于任何值且比任何值都小。
提示
此处官方规范描述为:如果任一操作数为#nan,则所有关系运算符的结果都为false。
但实际Excel测试结果为#nan永远最小。
- 当两个操作数都是数字且不是
#nan时,比较关系为-#infinity<...<-0.0=+0.0<...<infinity。
条件逻辑运算符
条件逻辑运算符有and和or。
logical-or-expression:
logical-and-expression
logical-and-expression or logical-or-expression
logical-and-expression:
is-expression
is-expression and logical-and-expression
逻辑与and仅操作数同时为true时,结果才为true;否则结果为fasle。
逻辑或or仅操作数同时为fasle时,结果才为false;否则结果为true。
条件逻辑操作符使用逻辑短路机制,因此操作数的位置不同可能导致结果不同。
提示
逻辑短路
根据逻辑运算符的左操作数判断是否需要继续计算右操作数,如果没必要则直接返回结果。
假定在x op y中,x和y均为逻辑值。
- 对于
x and y,如果x是false,则直接返回false,否则继续计算y。 - 对于
x or y,如果x是true,则直接返回true,否则继续计算y。
下面的真值表,纵向为左操作符,横向为右操作符。
and真值表总结:
- 操作数同时为
true时结果为true。 - 遵循逻辑短路(
null为左操作数时会计算右操作数)并在符合预期结果的情况下尽可能返回右操作数。 - 只要
error被计算就会引发错误。
and | false | |||
|---|---|---|---|---|
false | ||||
false | false | false | false | false |
false | ||||
or真值表总结:
- 操作数都为
false时结果为false。 - 只要
error被计算就会引发错误。 false和null始终返回null。- 遵循逻辑短路(
null为左操作数时会计算右操作数)并在符合预期结果的情况下尽可能返回右操作数。
or | false | |||
|---|---|---|---|---|
false | false | |||
对于表达式x op y:
- 传播在计算表达式
x和y时引发的错误。 x和y只能是logical或null类型的值。
算术运算符
+、-、*、/被称为算术运算符。
additive-expression:
multiplicative-expression
additive-expression + multiplicative-expression
additive-expression - _multiplicative-expression
_multiplicative-expression:
metadata- expression
multiplicative-expression * metadata-expression
multiplicative-expression / metadata-expression
精度
为了尽可能保留来自各种源的数字相关信息,M中的数字使用多种表示形式进行存储。应用于数字的运算符会从一种表示形式转换为另一种表示形式。M支持两种精度:
| 精度 | 语义 |
|---|---|
Precision.Decimal | 128位(16字节)十进制表现形式。表示范围从±1.0e-28到±7.9e28,有效位数28-29。 |
Precision.Double | 符合IEEE 754-2008标准的64位(8字节)双精度浮点数。 |
相关信息
浮点数的机制导致其无法准确的计算小数,因此出现了如货币(或叫定点小数)的高精度小数类型,实际这类数字都是使用整数存储,在需要显示时才表现出小数。
执行算术运算的方式是:先选择一个精度,将两个操作数都转换成该精度,然后执行相应的计算,最后返回之前选择的精度下的结果。
不同精度之间的转换:
- 十进制转双精度会通过四舍五入转换到最接近的等效双精度。
- 双精度转十进制会通过四舍五入转换到最接近的等效十进制,如果溢出会得到正或负的
#infinity。
内置的算术操作符(+、-、*、/)使用双精度。标准库函数(Value.Add、Value.Subtract、Value.Multiply、Value.Divide)可以指定精度。还有一些函数也支持指定精度,如:List.Sum、List.Product、List.Average。
加法运算符
加法运算符(x + y)可以计算不同类型的值。
T表示date、time、datetime、datetimezone。
x | y | 结果 | 可调换 | 说明 |
|---|---|---|---|---|
number | number | number | 是 | 数值求和 |
duration | duration | duration | 是 | 持续时间求和 |
T | duration | T | 是 | T偏移时间 |
T、number、duration、null | null | null | 是 |
非上表中的类型的操作数会引发错误。
传播在计算操作数时引发的错误。
数值求和
对两个数值进行加法计算,得到一个新的数值。
操作符+使用默认的双精度,如果需要十进制精度需要使用Value.Add。
双精度计算使用64位二进制双精度IEEE754算法的规则计算。
+ | y | +0 | -0 | +∞ | -∞ | #nan |
|---|---|---|---|---|---|---|
x | x+y | x | x | +∞ | -∞ | #nan |
+0 | y | +0 | +0 | +∞ | -∞ | #nan |
-0 | y | +0 | -0 | +∞ | -∞ | #nan |
+∞ | +∞ | +∞ | +∞ | +∞ | #nan | #nan |
-∞ | -∞ | -∞ | -∞ | #nan | -∞ | #nan |
#nan | #nan | #nan | #nan | #nan | #nan | #nan |
十进制精度求和时会尽可能使用精度最大的操作数的精度。尽可能是指:如果一个29位整数和一个小数计算,则结果无法带小数。
持续时间之和
两个持续时间之和即表示单位是100ns的时间刻度总数的和。
持续时间的日期时间偏移
T类型的值和duration类型的值求和表示对T类型的值进行偏移并得到T类型的值。类型T可以是date、time、datetime、datetimezone。
- 对
time和date类型的值进行偏移可以认为是datetime类型的值进行偏移,然后截取需要的部分。 - 对
datetimezone类型的值进行偏移不会改变时区部分。
减法运算符
减法运算符(x - y)可以计算不同类型的值。
T表示date、time、datetime、datetimezone。
x | y | 结果 | 可调换 | 说明 |
|---|---|---|---|---|
number | number | number | 是 | 数值相减 |
duration | duration | duration | 是 | 持续时间相减 |
T | duration | T | 否 | 偏移T类型值。 |
T、number、duration、null | null | null | 是 | |
T | 类型同左的T | duration | 是 | 两个相同T类型的值的时间差 |
两个datetimezone类型的值的计算会先对日期时间进行修正,然后再计算。
非上表中的类型的操作数会引发错误。
传播在计算操作数期间引发的错误。
数值差
对两个数值进行减法计算,得到一个新的数值。
操作符-使用默认的双精度,如果需要十进制精度需要使用Value.Subtract。
双精度计算使用64位二进制双精度IEEE754算法的规则计算。
- | y | +0 | -0 | +∞ | -∞ | #nan |
|---|---|---|---|---|---|---|
x | x-y | x | x | -∞ | +∞ | #nan |
+0 | -y | +0 | +0 | -∞ | +∞ | #nan |
-0 | -y | -0 | +0 | -∞ | +∞ | #nan |
+∞ | +∞ | +∞ | +∞ | #nan | +∞ | #nan |
-∞ | -∞ | -∞ | -∞ | -∞ | #nan | #nan |
#nan | #nan | #nan | #nan | #nan | #nan | #nan |
十进制精度求和时会尽可能使用精度最大的操作数的精度。尽可能是指:如果一个29位整数和一个小数计算,则结果无法带小数。
持续时间之差
两个持续时间之差即表示单位是100ns的时间刻度总数的差。
持续时间的日期时间偏移
T类型的值和duration类型的值求和表示对T类型的值进行偏移并得到T类型的值。类型T可以是date、time、datetime、datetimezone。
- 对
time和date类型的值进行偏移可以认为是datetime类型的值进行偏移,然后截取需要的部分。 - 对
datetimezone类型的值进行偏移不会改变时区部分。
两个日期时间的持续时间
- 两个相同的
date、time、datetime类型的值直接减去对应的日期时间部分,结果可以是负数。 - 两个
datetimezone类型的值相减,会先修正时区,然后相减,结果可以是负数。
乘法运算符
乘法运算符(x * y)可以计算不同类型的值。
x | y | 结果 | 可调换 | 说明 |
|---|---|---|---|---|
number | number | number | 是 | 数值乘积 |
duration | number | duration | 是 | 持续时间的倍数 |
number、duration、null | null | null | 是 |
非上表中的类型的操作数会引发错误。
传播在计算操作数时引发的错误。
数值乘积
对两个数值进行乘法计算,得到一个新的数值。
操作符*使用默认的双精度,如果需要十进制精度需要使用Value.Multiply。
双精度计算使用64位二进制双精度IEEE754算法的规则计算。
* | +y | -y | +0 | -0 | +∞ | -∞ | #nan |
|---|---|---|---|---|---|---|---|
+x | x*y | -x*y | +0 | -0 | +∞ | -∞ | #nan |
-x | -x*y | x*y | -0 | +0 | -∞ | +∞ | #nan |
+0 | +0 | -0 | +0 | -0 | #nan | #nan | #nan |
-0 | -0 | +0 | -0 | +0 | #nan | #nan | #nan |
+∞ | +∞ | -∞ | #nan | #nan | +∞ | -∞ | #nan |
-∞ | -∞ | +∞ | #nan | #nan | -∞ | +∞ | #nan |
#nan | #nan | #nan | #nan | #nan | #nan | #nan | #nan |
十进制精度求和时会尽可能使用精度最大的操作数的精度。尽可能是指:如果一个29位整数和一个小数计算,则结果无法带小数。
持续时间的倍数
持续时间与数字的倍数即表示单位是100ns的时间刻度总数重复一定次数的总长度。
除法运算符
除法运算符(x / y)可以计算不同类型的值。
x | y | 结果 | 可调换 | 说明 |
|---|---|---|---|---|
number | number | number | 是 | 数值乘积 |
duration | number | duration | 是 | 持续时间的分数 |
duration | duration | number | 是 | 持续时间的商 |
number、duration、null | null | null | 是 |
非上表中的类型的操作数会引发错误。
传播在计算操作数时引发的错误。
数值除法
对两个数值进行乘法计算,得到一个新的数值。
操作符*使用默认的双精度,如果需要十进制精度需要使用Value.Multiply。
双精度计算使用64位二进制双精度IEEE754算法的规则计算。
* | +y | -y | +0 | -0 | +∞ | -∞ | #nan |
|---|---|---|---|---|---|---|---|
+x | x/y | -x/y | +∞ | -∞ | +0 | -0 | #nan |
-x | -x/y | x/y | -∞ | +∞ | -0 | +0 | #nan |
+0 | +0 | -0 | #nan | #nan | +0 | -0 | #nan |
-0 | -0 | +0 | #nan | #nan | -0 | +0 | #nan |
+∞ | +∞ | -∞ | +∞ | -∞ | #nan | #nan | #nan |
-∞ | -∞ | +∞ | -∞ | +∞ | #nan | #nan | #nan |
#nan | #nan | #nan | #nan | #nan | #nan | #nan | #nan |
十进制精度求和时会尽可能使用精度最大的操作数的精度。尽可能是指:如果一个29位整数和一个小数计算,则结果无法带小数。
持续时间除法
两个持续时间之商即表示单位是100ns的时间刻度总数的商。
缩放持续时间
将持续时间缩放一定倍数。
结构组合
组合运算符(x & y)可以使用多种类型:
x | y | 结果 | 可调换 | 说明 |
|---|---|---|---|---|
text | text | text | 是 | 字符串拼接 |
date | time | datetime | 是 | 日期时间拼接 |
text、date、time、null | null | null | 是 | |
list | list | list | 是 | 拼接列表 |
record | record | record | 是 | 拼接记录 |
table | table | table | 是 | 拼接表 |
- 其它不在表中的类型合并会报错。
连接
使用x & y可以连接文本、列表、记录或表。
- 传播在计算表达式
x或y时引发的错误。 - 如果
x或y的项包含错误,错误不会传播。 null与text或null连接会得到null;与list、record、table连接会报错。- 连接
list是在左操作数的结尾拼接上右操作数。 - 连接
record是在左操作数的结尾拼接上右操作数中左操作数没有的字段。如果有共有的字段名,右操作数的字段值会替换左操作数的字段值。 - 连接
table时,不是公共列的列按照原始的左右顺序排列,表行会按照原始顺序上下拼接,左表在上,右表在下。公共列的单元格值,以右操作数中值为准。空白的部分填充null值。- 表最终的行数两表行数之和,列数是去重列名的列数之和。
- 如果两个表的列名相同,相当于右表拼在左表下。
- 如果两个表没有公共列,右表将拼接在左表的右下方,其余位置填充
null值。
// "ABCD"
"AB" & "CD"
// {1, 2, 1, 2}
{1, 2} & {1, 2}
// [a = 3, b = 2, c = 4]
[a = 1, b = 2] & [a = 3, c = 4]
// 结果如下表
#table({"a", "b"}, {{1, 2}, {3, 4}}) & #table({"c", "b"}, {{5, 6}, {7, 8}, {9, 10}})
合并表结果,来自b表结果为红色。
| a | b | c |
|---|---|---|
| 1 | 2 | null |
| 3 | 4 | null |
null | ||
null | ||
null |
合并
记录合并
可以使用x & y拼接两个记录。
- 传播在计算表达式
x或y时引发的错误。 - 记录字段内的错误不会传播。
- 合并记录不会导致字段值计算。
- 如果
x和y中有字段名重复,则x中的重复字段使用y中对应的字段值。 - 生成的记录顺序是
x后跟y中不属于x的字段,顺序与y的原始顺序一样。
// [a = 1, b = 2]
[a = 1] & [b = 2]
// [a = 3, b = 2, c = 4]
[a = 1, b = 2] & [a = 3, c = 4]
日期时间合并
使用x & y可以将date类型的值和time类型的值拼接为datetime类型的值。
- 传播在计算表达式
x或y时引发的错误。
// 0001-01-01T02:02:02.2220000
#date(1, 1, 1) & #time(2, 2, 2.222)
#time(2, 2, 2.222) & #date(1, 1, 1)
一元运算符
一元运算符包括:+、-、not。
unary-expression:
type-expression
+ unary expression
- unary expression
not unary expression
一元加运算符
一元加运算符(+x)可以使用不同类型:
x | 结果 | 说明 |
|---|---|---|
number | number | 一元加 |
duration | duration | 一元加 |
null | null |
// #nan
+ #nan
- 传播计算表达式
x时引发的错误。 - 一元加操作符的结果是操作数本身。
一元减运算符
一元减运算符(+x)可以使用不同类型:
x | 结果 | 说明 |
|---|---|---|
number | number | 一元减 |
duration | duration | 一元减 |
null | null |
- 传播计算表达式
x时引发的错误。 - 一元减操作符的结果是操作数的负值,某些值可能还是本身,比如:
#nan、null。
逻辑取反运算符
一元取反运算符(not x)可以使用不同类型:
x | 结果 | 说明 |
|---|---|---|
logical | logical | 一元取反 |
null | null |
- 传播计算表达式
x时引发的错误。 - 一元取反操作符只能是逻辑值或
null值。
not false // true
not true // fasle
not null // null
类型运算符
运算符is和as称为类型运算符。
类型兼容性运算符
类型兼容运算符(x is y)定义:
x | y | 结果 |
|---|---|---|
any | nullable-primitive-type | logical |
如果x值的归属类型兼容于类型y,则表达式x is y返回true,否则返回false。y必须是nullable-primitive-type。
is-expression:
as-expression
is-expression is nullable-primitive-type
nullable-primitive-type:
nullableopt primitive-type
is运算符支持的类型兼容是常规类型兼容性的子集。
- 当
x为null时,仅当y是可为空的类型或any类型才是兼容的。 - 当
x不为null时,仅当x的原始类型与y相同才是兼容的。
在计算x is y时:
- 传播计算表达式
x或y时引发的错误。
类型断言运算符
类型断言运算符(x as y)定义:
x | y | 结果 |
|---|---|---|
any | nullable-primitive-type | any |
断言表达式x as y,根据is运算符判断值x的类型是否和y兼容,如果不兼容则会出现错误。y必须是nullable-primitive-type。
as-expression:
equality-expression
as-expression as nullable-primitive-type
表达式x as y计算如下:
- 执行类型兼容性检查
x is y,如果测试成功,则断言返回未修改的x。 - 如果兼容性检查失败,则引发错误。
在计算x as y时:
- 传播计算表达式
x或y时引发的错误。
合并运算符
如果合并运算符??的左操作数不为null,则将它作为结果返回,否则将右操作数作为结果返回。如果左操作数不是null,右操作数不会被计算。