在Power Pivot中进行的部分,接下来这半节,我们要进入最激动人心的
第1个:DAX 函数——查找父级索引
DAX函数是在 Power BI 或 Power Pivot (Excel) 中使用的一套函数。
= LOOKUPVALUE(
'BOM表'[索引],
'BOM表'[BOM实例ID], 'BOM表'[BOM实例ID],
'BOM表'[BOM层级], 'BOM表'[父级层级]
)
此处这个公式将用于创建一个“新建列(计算列)”。
为了让你彻底明白,我们采用“剥洋葱”的方式,由表及里、从宏观到微观一层层来拆解这段代码函数:
🧅 第一层:最外层的直观目的(它是干嘛的?)
这段公式的终极目的是:“找爸爸的身份证号”。
在 BOM(物料清单)这种树状结构的数据中,每一行代表一个零件。这段代码的作用是:去原表中寻找当前零件的“父级零件”,并把那个父级零件的“索引号(ID)”提取过来,填在当前行。
🧅 第二层:核心函数的作用(LOOKUPVALUE)
LOOKUPVALUE 是 DAX 中非常强大的查找函数。你可以把它理解为 Excel 里增强版的 VLOOKUP 或 XLOOKUP。它的超能力在于可以同时满足多个条件进行精确查找。
它的基本语法结构是:LOOKUPVALUE( 要返回哪个列的值, 条件列1, 查找值1, 条件列2, 查找值2, ... )
🧅 第三层:逐个参数拆解(代码详解)
我们把你的公式对号入座,看看它包含了哪些条件:
'BOM表'[索引](要返回的结果)
- 指令:如果找到了符合条件的那一行,请把那一行的
[索引]值给我拿过来。
'BOM表'[BOM实例ID], 'BOM表'[BOM实例ID](查找条件 1)
- 指令:去目标表的
[BOM实例ID]列里面找,找什么呢?找和当前行的[BOM实例ID]一模一样的数据。 - 业务含义:BOM表里可能存了成百上千个不同的产品结构(实例)。这个条件确保了:“我们只在当前属于的这个大产品(同一个BOM树)里面找,别串号找到别的产品那里去了。”
'BOM表'[BOM层级], 'BOM表'[父级层级](查找条件 2)
- 指令:在满足了上面条件的基础上,再去目标表的
[BOM层级]列里面找,找和当前行的[父级层级]数值一样的数据。 - 业务含义:假设当前零件是 3 级(BOM层级=3),它的父亲是 2 级(父级层级=2)。那么我就要去表里找
[BOM层级]列等于 2 的那一行。这个条件确保了:“找到的必须是我的上一级(父级)。”
不理解第2个条件的话进一步看下面的解释:
你在这里有困惑非常常见!这是几乎所有刚接触 DAX 函数的人都会卡住的地方(专业术语叫“行上下文”的理解)。
你觉得“3”和“2”不可能相等,对吧?你的直觉是对的,但你误解了公式匹配的对象。
核心原因在于:你误以为它是在“当前行自己跟自己比”。
实际上,LOOKUPVALUE 的工作模式是:“拿着当前行的线索,去整张表里找别人”。
为了让你秒懂,我们换个“警察帮小明找爸爸”的场景来模拟这个过程:
🕵️♂️ 角色分配
- 当前行(小明):就是公式正在计算的这一行。
- 整张表(茫茫人海):就是你要去查找的目标范围。
- LOOKUPVALUE(警察):负责拿着线索找人的机器。
🔍 公式拆解:逗号前后的秘密
公式的这一段是:'BOM表'[BOM层级], 'BOM表'[父级层级]
这两个参数代表着完全不同的视角:
- 逗号前面的
'BOM表'[BOM层级]: 代表“茫茫人海中,每个人的自身属性”(警察要查验的身份证字段)。 - 逗号后面的
'BOM表'[父级层级]: 代表“当前行(小明)手里拿着的线索”。
🎬 情景模拟(为什么是 2 匹配 2,而不是 3 匹配 2)
现在,公式运行到了小明(当前零件)这一行:
- 小明(当前行)的真实数据是:
[BOM层级]= 3 (意思是:我是第 3 代,孙子辈)[父级层级]= 2 (意思是:我爸爸是第 2 代,儿子辈)
- 小明去警察局报案(代入公式):
- 小明说:“警察叔叔,我要找我爸。不用管我是第几代(不用管我的3),我只提供一个线索——我爸是第 2 代(我的父级层级是2)。”
- 警察(LOOKUPVALUE)开始去街上找人:
- 警察拿着小明给的线索“2”,去街上(整张表)拦住路人挨个查身份证。
- 警察问路人甲:“你的
[BOM层级]是几?” - 路人甲:“我是 4。” (4 不等于 2,匹配失败,跳过)
- 警察又问路人乙(目标行):“你的
[BOM层级]是几?” - 路人乙:“我是 2。” (2 等于小明给的线索 2,匹配成功!)
- 最终结果:
- 警察确认路人乙就是小明要找的人,然后把路人乙的
[索引](也就是身份证号)提取出来,交给了小明。
- 警察确认路人乙就是小明要找的人,然后把路人乙的
💡 总结
所以,根本不是让 3 去匹配 2。当前行的 [BOM层级]=3 这个信息,在这个查找动作中根本就没有被用到!
系统是拿着“当前行的 [父级层级](也就是 2)”,去寻找“另一行的 [BOM层级](等于 2 的那一行)”。
是 2 去匹配 2。 只是这两个 2 属于不同的行(一个是找人的线索,一个是被找人的自身属性)。
这样解释,是不是拨云见日了?
🧅 第四层:逻辑重组(大白话翻译)
把上面的条件组合起来,这段公式对数据库说的话就是:
“请在整个‘BOM表’里帮我找一行数据。
这行数据必须和我属于同一个产品(条件1:BOM实例ID相同),
并且这行数据的层级刚好是我的父级层级(条件2:BOM层级 = 我的父级层级)。
找到了之后,把那个父级零件的『索引』告诉我。”
🧅 最里层:隐藏的DAX机制与潜在风险(高手视角)
1. 为什么看起来像自己等于自己?
你可能会疑惑 ..., 'BOM表'[BOM实例ID], 'BOM表'[BOM实例ID], ... 这种写法。
在 DAX 的计算列中,存在“行上下文”。逗号前面的 'BOM表'[BOM实例ID] 代表整张表的这一列;逗号后面的 'BOM表'[BOM实例ID] 代表的是正在计算的当前这单行的这个值。系统会在后台自动区分。
2. 这个公式报错的风险在哪里?LOOKUPVALUE 有一个死穴:它要求找出的结果必须是唯一的。
如果在同一个 [BOM实例ID] 下面,有两个相同的 [BOM层级](比如同一个产品下面有两个并列的父级节点),系统就不知道该返回哪一个人的索引了,此时公式就会报错。
因此,写这个公式的前提是:在同一个 BOM实例中,每一个父级层级只能对应唯一的一个索引节点。 如果存在一个父级下有多个同层级节点的情况,这个公式需要修改逻辑或结合其它辅助列来保证唯一性。
第2个:DAX 函数——绘制家谱路径
接下来,结合上一张图,我们现在来到了处理 BOM(物料清单)或组织架构等“父子层级结构(Parent-Child Hierarchy)”时最核心、最经典的一步。
= PATH('BOM表'[索引], 'BOM表'[父项索引])
我们继续用“剥洋葱”的方式,层层拆解这个非常神奇的 PATH 函数:
🧅 第一层:最外层的直观目的(它是干嘛的?)
这段公式的终极目的是:“查户口” 或者说 “画出这个零件的完整族谱”。
在数据表里,零件之间只有“谁是谁的直接爸爸(父项)”这种单线联系。但实际业务中,我们经常需要知道:“这个底层的小螺丝钉,到底是属于哪个大组件,最终又属于哪个成品的?”PATH 函数的作用,就是把这种碎片化的父子关系,串成一条从老祖宗到当前零件的完整路径。
这个新计算而成的 [家谱路径] 计算列会生成类似 9726|9804 这样的爷 | 爸 | 孙的层级关系文本串。
🧅 第二层:核心函数的作用(PATH)
PATH 是 DAX 中专门为解决“层级扁平化”而量身定制的专用函数。
如果没有它,要在关系型数据表里找出一个节点的所有上级,需要写极其复杂的循环代码。而 PATH 能够自动顺藤摸瓜,一直往上找,直到找到没有上级的“老祖宗(根节点)”为止。
它的语法极其简单,只有两个参数:PATH( 当前节点的ID列, 当前节点的父级ID列 )
🧅 第三层:逐个参数拆解(代码详解)
对应到你的公式 = PATH('BOM表'[索引], 'BOM表'[父项索引]),我们来看看它要求传入什么:
'BOM表'[索引](参数1:我是谁?)
- 指令:告诉系统,代表当前这一行数据(这个零件)唯一身份的身份证号(ID)是哪一列。在你的表里,就是
[索引]。
'BOM表'[父项索引](参数2:我爸爸是谁?)
- 指令:告诉系统,记载当前零件的直接上一级(父亲)身份的列是哪一个。在你的表里,这正是上一张图那个逻辑(或者是类似的查找逻辑)找出来的
[父项索引]。
🧅 第四层:底层运行逻辑(结合你的截图数据)
我们拿你截图里的第一行数据来做个“慢动作回放”,看看系统在后台是怎么跑的:
- 系统看第一行:当前零件的
[索引]是 9804。 - 找爸爸:系统看了眼
[父项索引],发现它的直接上级是 9726。 - 继续往上找(递归):系统不会停下,它会在整张表里继续找
[索引]为 9726 的那一行,想看看 9726 的爸爸是谁。 - 到达顶点:假设 9726 就是顶层成品(例如那个“儿童学习椅”),它的
[父项索引]是空的(或者为 0,表示没有上一级了)。 - 生成结果:系统停止查找,把刚才经过的节点按从大到小的顺序用竖线
|连起来。 - 最终输出:于是这一行就得到了结果:
9726|9804。(意思是:我是 9804,我是 9726 的直接下属)。
如果是更深的层级,比如 9804 下面还有一个 10001,那么 10001 的路径就会变成 9726|9804|10001。
🧅 最里层:隐藏的DAX机制与潜在风险
看似简单的两个参数,其实埋着几个非常严格的“暗雷”,这也是很多新手经常报错的地方:
1. 数据类型必须绝对一致![索引] 和 [父项索引] 这两列的数据类型必须完全一样。你要么都是“文本型”,要么都是“整数型”。如果一个是文本一个是数字,哪怕看起来长得一样,PATH 也会立刻报错罢工。
2. 绝对不能出现“死循环”(Circular reference)!
大自然中,儿子不能是爸爸的爸爸。在数据里也一样:如果 A 的父项是 B,B 的父项是 C,结果你数据填错了,C 的父项填了 A。PATH 在顺藤摸瓜时就会陷入无限死循环,此时 DAX 引擎会直接报错。
3. 一个儿子只能有一个亲生爸爸!PATH 函数不支持“多重父级”结构。在同一个体系下,一个 [索引] 只能对应唯一的一个 [父项索引]。如果有交叉,它处理不了。
💡 进阶延伸:
生成了这个带有竖线的 9726|9804 有什么用呢?
它的最大价值是作为“跳板”。接下来,你可以配合 PATHITEM 函数(提取路径中的第几个节点)、PATHLENGTH 函数(计算这个零件处在第几个层级)或者 PATHCONTAINS 函数,极其方便地在 Power BI 或数据透视表中实现诸如“展开/折叠”、“计算某一大类下的所有物料成本”等高级功能!下面的计算穿透用量就是用到这个PATHCONTAINS 函数!










暂无评论内容