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 级别。
首先,我是幂查询的新手,所以我正在迈出第一步。但是我需要尝试在工作的某个时候交付,这样我就可以获得一些喘息的时间来学习。
我有以下 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 级别。