WPS Query之浅谈类型

💡

下文中Wps Query简称WQ,Power Query简称PQ。

💡

截止目前(202608)WQ并未开放文档,本文更多基于PQ的经验来描述相关内容。

前言

WQ/PQ使用m语言运行,m语言使用强类型。之所以为强类型是因为PQ前后端经常会遇到数据库,除了常见的数字、日期等类型外,为了兼容数据库还设计了更细分的整数、货币等用以方便将数据导出到数据库,另外还有惰性计算和折叠等机制来提高性能。

分类

目前PQ中对用户开放的类型有anyanynonnullbinarydatedatetimedatetimezonedurationfunctionlistlogicalnonenullnumberrecordtabletexttimetype

💡

绿色标记是经常会遇到的类型,黄色标记是不太常见或稍微有点难以理解的类型,其它为不需要关注的类型。

类型兼容

any类型是一个特殊的类型,它兼容所有的值。对于表达式1 is number毫无疑问是true,同时1 is any也是true,包括null is any也是true

如果您翻阅过PQ文档中关于is关键字的描述:

Test if type is compatible with primitive type or nullable primitive type
测试类型是否兼容基本类型或可为空的基本类型

基于is关键字的描述,我们可以说any类型兼容1,但不能说1any类型。

💡

any类型并不存在实际的对应值,但它兼容所有值。

在WQ/PQ中,当程序推测到列中存在多种类型时,m引擎会自动将列类型设为any。比如下图这种图标就表示any类型:

对应的查询内容如下:

let
    源 = Excel.Workbook(File.Contents("C:\Users\Black\Desktop\wq测试.xlsx"), null, true),
    导航 = 源{[Name = "Sheet1"]}[Data],
    提升的标题 = Table.PromoteHeaders(导航, [PromoteAllScalars = true]),
    修改列类型 = Table.TransformColumnTypes(提升的标题, {{"类型测试", type any}})
in
    修改列类型

转换列类型

当用户点击列左上角的类型图标转换类型后,m引擎调用Table.TransformColumnTypes函数对指定的列进行类型转换,但这里发生了两步:

  1. 使用对应的.From函数转换值,比如:A列指定为小数,那么会使用Number.From函数对A列的所有值进行转换

  1. 标记指定列的类型为设置的类型

截止完帖,WQ中的日期类型还无法正确转换"20260807"这种日期,但Date.From函数可以正常转换。这是一个bug,后续应该会修复。

很多转换函数都支持指定区域设置,如果您涉及国际业务,这很重要!

  • Number.From("12,34", "zh-CN")1234

  • Number.From("12,34", "de-DE")12.34

当用户转换列类型时会转换值类型设置列类型,然后列图标变成了对应的类型图标,因此很多用户会错误的认为图标(列类型)就是实际的列类型,这是一个误区:PQ界面中表列左上角的类型在PQ中并不能约束列的类型。具体如下图所示:

💡
  1. 只有用户手动更改表达式才会出现上图情形,所以通常不需担心该问题。

  1. 由于WQ当前并未完全实现所有函数,也并未开放编辑器,暂时无法测试。

  1. listrecord也存在类似表的类型。

PQ和WQ中,当用户添加新列后,不会指定类型(即默认any):

在当前PQ和未来WQ开放编辑栏后,如果用户能够确定列类型,则可以手动指定类型。

💡

手动指定类型需要用户有足够的自信,因为Table.Column中指定类型并不会对列中的值进行类型转换,因此它可能影响后续步骤的结果。但在最终导出结果时还会进行一次数据转换,具体见下一小节。

可为空类型

在阅读转换列类型小节时不知道大家都是思考过一个问题:如果将列设置为数字,而列中的null转换后为何没有报错?在解释Table.TransformColumnTypes执行时,第一步是:

使用对应的.From函数转换值,比如:A列指定为小数,那么会使用Number.From函数对A列的所有值进行转换

由于.From函数的机制就是如果输入null则输出null,因此最终可以得到null

WQ/PQ中,类型可以使用nullable关键字修饰类型来表示该类型兼容null值,常见的能够很容易看出来区别的是函数:

// 正确,因为123属于number类型
((a as number) => a)(123)
// 错误,因为null不属于number类型
((a as number) => a)(null)
// 正确,因为nullable修饰的类型兼容null
((a as nullable number) => a)(null)
💡

在完成帖子时,WQ还存在显示BUG,上述表达式可以在条件列中执行,但如果编辑会出问题:

  • as会变为as as

  • nullable number会变为number?

数据导出

前文提到:

PQ界面中表列左上角的类型在PQ中并不能约束列的类型

列类型在WQ/PQ中并非约束,而是在导出(或叫上传)数据时由接收端根据类型进行处理。

当我们将设置了类型的列上传后,接收端会对按指定类型进行检查和转换(如果是any类型则会检查最大的兼容类型)。比如前文中未设置类型的例子:

由于表格能够兼容例子中的类型,因此会原样输出(例子中的FALSE为字符串)。由于不兼容错误,如果存在错误就会显示为空白。

这种转换机制并非是万无一失的,极端情况下会转换成错误的结果。

但如果上传到PP(Power Pivot)中,PP接收到any类型后会检查所有值,找出合适的类型并转换值的类型,这个例子会将列中的所有值导出为文本格式,因为PP不支持混合类型的列。

一些建议

虽然WQ与PQ尽量保持兼容,但因为现不同,目前发现有些极端情况会出现不同结果,在转移平台时请谨慎检查。为了避免这种极端,请务必选择合适的类型:仅需要日期就不要使用日期时间,需要货币就不要使用小数。

关于具体的类型及可能出现的不兼容会在以后的帖子中详尽讲解。

浏览 80
收藏
点赞
分享
+1
+1
全部评论