Table.AddJoinColumn()可以抽象地概括为function(table1 as table, key1 as any, table2 as any, key2 as any, newColumnName as text) as table,大概的意思为该函数的第一个参数为需要进行左连接的左表,第二个参数为左表用来连接右表的键,可以选择"键名称"或者{"键名称".."键名称"}这两种形式中的一个填写第二个参数,第三个参数为需要进行左连接的右表,第四参数为右表用来连接左表的键,可以选择"键名称"或者{"键名称".."键名称"}这两种形式中的一个填写第四个参数,最后一个变量需要通过文本指定连接后生成的表格列的名称。假如Table.AddJoinColumn()运行时间与Table.NestedJoin()相当,那么可以认为前者为后者的语法糖衣,让使用者在编辑M语言时少填一个参数。左链接在很多情况中都能够起到化繁为简的作用,接下来将通过Table.AddJoinColumn()说明这一点。
(案例1)假如现在需要完成从左表(Case1_DB)到右表的转换:
因为左表的每一行都刚好组成一对不重复的组合,可以使用左连接实现以上转换:
let Source= Table.Group( Case1_DB, "年级", { "班级", each Record.Field( Table.First( _ ), "班级" ), Int64.Type } ), LeftJoin= Table.AddJoinColumn( Case1_DB, {"年级", "班级"}, Source, {"年级", "班级"}, "NTable" ), SelectCols= Table.SelectColumns( LeftJoin, {"NTable", "班级"} ), Expansion= Table.ExpandTableColumn( SelectCols, "NTable", {"年级"} ) in Expansion
以上代码的第一个步骤Source使用了Table.Group取出每一个年级的第一个班级,这样就构造好左连接需要的右表。然后,使用Table.AddJoinColumn()完成左表为Case1_DB以及右表为Source的左连接。接着使用Table.SelectColumns()移除不需要的列以及调整列的次序,最后使用Table.ExpandTableColumn()展开来自右表的班级就完成所需要的转换。
(案例2)某企业商品价格每周五更新一次,在2020年2月29日系统生成的价格表(Case2_Quotation)如下:
现在需要根据同一个月的销售量表(Case2_Sales),计算该月份的总销售额。
案例2一般使用模糊匹配或者构造辅助表的方式进行解答,为了配合主题接下来将讲解辅助表的制作方法(MinDate为2020-01-31,MaxDate为2020-02-29):
let Source= Table.FromValue( List.Dates( MinDate, Duration.TotalDays( Value.Subtract( MaxDate, MinDate ) ) + 1, #duration( 1, 0, 0, 0 ) ), [ DefaultColumnName = "Date" ] ), LeftJoin= Table.AddJoinColumn( Source, "Date", Case2_Quotation, "Date", "NTable" ), Expansion= Table.ExpandTableColumn( LeftJoin, "NTable", {"Price"} ), FillDown = Table.FillDown( Expansion, {"Price"} ), DataType = Table.TransformColumnTypes( FillDown, { "Date", type date } ) in DataType
Source步骤利用List.Dates()生成了辅助表的所有日期,生成的日期串列传入Table.FromValue()得到的表格为左表。该表通过Table.AddJoinColumn()完成对Case2_Quotation的左连接,展开后使用Table.FillDown()就可以使每一个周五商品的价格在要求的日期中重复。最后,使用内连接把Case2_Sales与上表连接起来就可以计算总销售额。
读者如果十分需要技术壁垒,可以考虑以下代码制作辅助表:
let Source= List.Combine( { Case2_Quotation[Date], { Date.AddDays( MaxDate, 1 ) } } ), Transformation1= List.Transform( List.Numbers( 0, List.Count( Source ) - 1 ), each List.Combine( { List.Range( Source, _, 2 ), { Case2_Quotation[Price]{ _ } } } ) ), Transformation2= List.Transform( Transformation1, (x) ⇒ List.Transform( List.Dates( x{ 0 }, Duration.TotalDays( x{1} - x{0} ), #duration( 1, 0, 0, 0 ) ), (y) ⇒ { y, x{2} } ) ), ToTable = Table.FromRows( List.Combine( Transformation2 ), type table [ Date = date, Price = number ] ) in ToTable
由于商品价格重复的规律,需要把时间按上图进行切割。这需要分几步才能完成的,首先需要构造以下串列:
代码中Source步骤就是用来构造上图所示的串列,不难发现如果把以上串列中的第一个元素和第二个元素展开为连续的日期然后移除最后一日即可以得到最终结果按商品价格进行分组后的第一组所横跨的所有日期。第二元素与第三元素对应的是第二组的所有日期,第三元素与第四元素对应的是第三组的所有日期,其他如此类推。也就是说以上串列除去最后一个元素的其他元素,需要与紧随着自身的元素进行配对。因为后续还涉及到在每一对日期展开后的时间序列中重复当前周五的商品价格,还需要在每一对日期组成的串列后添加对应的商品价格,这些都是由Transformation1步骤完成的。
Transformation2步骤的最外层的List.Transform()负责把上图构造好的每一个串列依次传入到内层的List.Transform()。然后,在内层的List.Transform()的第一个参数中利用传入的头两个元素完成日期的展开,并在其第二个参数中完成重复商品价格的工作。
辅助表的第二个解法,函数都看得懂,合起来就看不懂了……
已经重新整理
感谢大神讲解
老师你好,如果我想在A表中通过ID字段对应关系从B表中匹配数据作为新列添加在A表中,该如何写呢?
=Table.AddJoinColumn( TableA, "ID", TableB, "ID", "嵌套的表B")