Power Query M 循环 table / 通过自连接查找

Power Query M loop table / lookup via a self-join

首先,我是幂查询的新手,所以我正在迈出第一步。但是我需要尝试在工作的某个时候交付,这样我就可以获得一些喘息的时间来学习。

我有以下 table(示例):

Orig_Item       Alt_Item    
5.7             5.10
79.19           79.60
79.60           79.86
10.10           

我需要创建一个列来循环 table 并显示最后的 Alt_Item。所以结果如下:

Orig_Item       Alt_Item        Final_Item  
5.7             5.10            5.10    
79.19           79.60           79.86   
79.60           79.86           79.86   
10.10           

非常感谢

实际上,这对于初次体验 Power Query 的人来说太复杂了。

如果那是您必须要做的,那就这样吧,但您应该意识到,您正从一项相当困难的任务开始。

小细节:我预计最后一个 Final_Item 是 10.10。根据示例,如果 Alt_Item 为空,则 Final_Item 将为空。如果这不正确,那将是您相应调整下面代码的良好第一步。

您可以创建一个新的空白查询,将此代码复制并粘贴到高级编辑器中(替换默认代码)并将源调整为您的 table 名称。

let
    Source = Table.Buffer(Table1),
    AddedFinal_Item = 
      Table.AddColumn(
        Source, 
        "Final_Item", 
        each if [Alt_Item] = null
             then null
             else List.Last(
                    List.Generate(
                        () => [Final_Item = [Alt_Item], Continue = true],
                        each [Continue],
                        each [Final_Item = 
                                    Table.First(
                                        Table.SelectRows(
                                            Source, 
                                            (x) => x[Orig_Item] = [Final_Item]),
                                        [Alt_Item = "not found"]
                                                        )[Alt_Item], 
                              Continue = Final_Item <> "not found"],
                        each [Final_Item])))
in
    AddedFinal_Item

此代码使用函数 List.Generate 来执行循环。 出于性能原因,在调用 List.Generate.

之前,table 应始终缓冲在内存中 (Table.Buffer)

List.Generate 是最复杂的 Power Query 函数之一。

它需要 4 个参数,每个参数本身就是一个函数。

在这种情况下,第一个参数以 () 开头,另外 3 个参数以每个开头(从上面的大纲中应该很清楚:它们是对齐的)。

参数 1 定义初始值:包含字段 Final_Item 和 Continue.

的记录

参数2是继续的条件:如果找到一个项目。

参数 3 是每次迭代中的实际转换:在源 table 中搜索(使用 Table.SelectRows)一个 Orig_Item 等于至 Alt_Item。这包含在 Table.First 中,其中 returns 第一条记录(如果找到的话),如果没有找到则接受默认值,在这种情况下,字段 Alt_Item 的记录值为 "not found",从这个结果返回记录字段 [Alt_Item] 的值,它是第一条记录的值,或者是默认值的 "not found"。

如果值为 "not found",则 Continue 变为 false,迭代将停止。

参数 4 是将返回的值:Final_Item.

List.Generate returns 每次迭代的所有值的列表。只有最后一个值是必需的,因此 List.Generate 包含在 List.Last.

结语:Power Query 中很少需要实际循环,我认为应尽可能避免。然而,在这种情况下,这是一个可行的解决方案,因为您事先不知道会遇到多少 Alt_Item。 List.Generate 的替代方法是使用 递归函数 List.Accumulate 也接近于循环,但迭代次数是固定的。

这可以简单地通过自连接来解决,悬而未决的问题是您将支持多少层间接寻址。

假设只有一层间接寻址,Orig_Item上没有重复,解决方案是:

let
    Source = #"Input Table",
    SelfJoin1 = Table.NestedJoin( Source, {"Alt_Item"}, Source, {"Orig_Item"}, "_tmp_" ),
    Expand1   = ExpandTableColumn( SelfJoin1, "_tmp_", {"Alt_Item"}, {"_lkp_"} ),
    ChkJoin1  = Table.AddColumn( Expand1, "Final_Item", each (if [_lkp_] = null then [Alt_Item] else [_lkp_]), type number)
in
    ChkJoin1  

这可以通过常规 UI 实现,使用合并查询,然后展开列并添加自定义列。

如果你想支持多级间接寻址,把它变成一个被调用 X 次的函数。对于数据驱动的间接级别,您将调用包装在 list.generate 中,将中间表放入结构化列中,尽管这是更高级的 PQ 级别。