引子 前文说道: 生成UUID论英雄:Python、MySQL和Power BI谁强? 在用Text.NewGuid生成GUID时发现,这一列的值全都是相同的: 也就是说,“得益于”pq强大的引擎,Text.NewGuid()只运行了一次,的确非常节省算力。 我们也可以据此推测,PQ中的添加自定义列,首先会检查表达式,引擎发现每一行都是一个Text.NewGuid(),既然是一样的,因此就计算一次填充到了所有行。 如果,每一行的输出结果是不同的呢,比如每一行是GUID和姓名: = Table.AddColumn(删除的副本, '自定义', each Text.NewGuid()&[NAME]) 结果也是相同: 如果我们在列上加一个判断: = Table.AddColumn(源, '自定义', each if Text.Length([NAME])>2 then Text.NewGuid() else Text.NewGuid()) 就会发现:Text.NewGuid()运行了2次: 2个字的马云是c9开头,三个字的马斯克是00开头。 那么可以说明:pq的引擎并不是对每一行单独进行整个公式的运算,而是先进行if判断,如果为真,把所有为真的行标记出来,统一进行一次Text.NewGuid()计算,剩下的行再统一进行一次Text.NewGuid()计算。 而不是像上面那样先对Text.NewGuid()进行一次运算后再判断。 总之,以上两种办法,我们是无法得到随机生成的每一行不同的GUID的。 根本原因就是在于pq的引擎太过于强大与智能,此文也有过相关说明: 只能说,节省了计算机和云计算的算力,却消耗了大量的脑力。 正文开始 想到Power Query是有循环函数的:Transform类函数。 如列表循环:List.Transform;表循环函数:Table.TransformColumns;记录循环:Record.TransformFields。 循环就是将容器中的元素一个一个的拿出来,依次进行操作,例如我们构建一个{1,2,3}的列表,现在需要将列表中的每个元素都进行*10的运算,如下图 使用列表循环函数List.Transform,该函数的意思就是将列表{1,2,3}中的每个元素依次循环出来,赋予给后面的下划线,列表有几个元素,该函数就进行几次操作。
循环函数最终得到的元素个数和初始列表的个数一致。 我们可以使用这个思路进行操作: 首先将table转为list: 然后对list进行循环运算: = List.Transform(NAME,(x)=>Text.NewGuid()) 结果: wonderful! 但是,这种写法只会出现一列GUID的list,并不是姓名和GUID的组合,我们试着改写一下: = List.Transform(NAME,(x)=>x&','&Text.NewGuid()) 得到: 将其转为表,并使用逗号分隔符分为两列: 结果: 大功告成! 本文结束。 关闭并应用: 学谦: 这搞了个寂寞啊?! 实在是没招了,想起来大海老师的一篇文章: 那就不重复造轮子了,按着这个改吧。 解决方案 方法一 ![]() 然后再将两列索引删除,即可获取NAME和GUID列: 方法二 ![]() 修改语句,对引用的表套个Table.Buffer函数,再对结果再套一个Table.Buffer函数: 方法三 ![]() 使用List.Random函数来生成随机列,可以达到生成随机数的目的: = Table.AddColumn(已添加自定义, '自定义.1', each List.Random(1)) 这个的原理猜测应当是每一行都生成一个独立的list,然后展开为行的时候每一行都是独立的,因此得到的结果也是独立的。 根据这个思路,我们试着来写一下生成Text.NewGuid()的列表: = Table.AddColumn( 删除的副本, '自定义', each List.Transform({1},(x)=>Text.NewGuid())) 添加一列,每一列都是列表的转换,生成列表: 理论上,像List.Random函数一样,每一个list中的获取结果应当是不一样的,展开: 学谦: 我受够了! 理论上不应该啊! 本着不服输的精神,继续改一下公式: = Table.AddColumn( 删除的副本, '自定义', each List.Transform( List.Random(1), (x)=>Number.ToText(x)&','&Text.NewGuid() ) ) List.Random(1)每次生成的数是不同的,然而它旁边的Text.NewGuid()却只运算一次,我也是服了,我也是醉了: 再进一步,我们一次性list生成5个随机数: = Table.AddColumn( 删除的副本, '自定义', each List.Transform( List.Random(5), (x)=>Number.ToText(x)&','&Text.NewGuid() )) 结果: 这就不应该了! 本文一开始我们就写过一个公式,对list进行循环运算: = List.Transform(NAME,(x)=>Text.NewGuid()) 结果出现的每一行都是不同的GUID: 或者我们新建一个空查询,将生成list的那一部分单独拿出来: = List.Transform(List.Random(5),(x)=>Number.ToText(x)&','&Text.NewGuid()) 结果你会发现Text.NewGuid()同样运行了5次: 这就奇了怪了! 不过,从另一个侧面也可以看出来,本来我们以为List.Transform是可以循环的,但是在某些场景中,它暴露出来并不是循环的本质。 因此,解决办法3,无效。 方法四 ![]() Levi大神的指导: = Table.AddColumn(删除的副本, '自定义', each try error Text.NewGuid()) 返回的是嵌套的record,适当改进一下: = Table.AddColumn(删除的副本, '自定义', each (try error Text.NewGuid())[Error][Message]) 就直接得到结果了: 避免lazy evaluation! 方法五 ![]() 畅心大神的指导: = Table.AddColumn(删除的副本, '自定义', each {_,Text.NewGuid()}{1}) 这个是逻辑上是最简洁的,通过生成每一行不同列表的方式来获取第二个值。同样也是避免lazy evaluation! 畅神,yyds! 结论 在Power BI中生成UUID并不是一个很好的想法。 的确,它只是我在MySQL和Python中生成UUID时,临时突发奇想而来,看看PQ是否也能实现同样的目的,通过向几位大神请教,获得了解决办法和解决思路。 并且,pq的每一次刷新都会导致UUID改变,就像Jack S老师说的,除非用来做加密,否则真是没有什么用途。 在Power BI中处理带有UUID的表同样不是一个很好的想法。因为UUID非常长,很大程度上会明显增大模型的数据量,处理时间会拉长,影响效率。关于这一点,很早之前在用MySQL时就是一个问题,最近在Power BI中测试也是同样的结果。改天单独说明一下这个问题的改进措施。 |
|
来自: F2967527 > 《Power BI》