EP030 List.Distinct:财务BP如何搞定汽车零部件产线绩效排名(跳跃排名 vs 稠密排名)

文 / 老汪(汽车零部件财务与商业智能实战派)

大家好,我是老汪。

在咱们汽车零部件制造企业(Tier 1 一级供应商)月末开月度经营分析会的时候,做财务 BP、成本会计或者运营分析的朋友,经常会遇到一个让人头疼的场景:

车间主任、生产副总和厂长们坐在一起,桌上摆着各个车间(精密压铸、数控机加、冲压焊接)各条产线的完工量和单位边际贡献(单价减去单位变动成本)。为了衡量各条产线承接主机厂(比亚迪、吉利、特斯拉等)车型的盈利能力,财务需要给每条产线排个座次、核发月度绩效奖金。

但在汽车零部件实际生产中,经常会出现几条产线的单位边际贡献刚好一模一样的情况(比如压铸一线的铝合金下摆臂,和机加二线的双活塞制动卡钳总成,单位边际贡献都是 3200 元/套,并列第一)。

这时候考核委员会的争议就来了:
– 如果按照国际通用的跳跃排名(Rank):并列第 1 名之后,下一条产线直接跳到了第 3 名(中间第 2 名空缺)。机加车间主任立马不干了:“老汪,我们明明只比第一名差一点点,怎么直接掉到第三名去了?第二名被狗吃了?”
– 但如果按咱们中国本土习惯的“连续稠密排名”(Dense Rank):并列第 1 名之后,下一条产线紧接着算作第 2 名(名次不空,按阶梯拿奖),大家心服口服。

很多财务朋友在 Excel 里写了极其复杂的 COUNTIF 加条件嵌套,甚至用 VBA 宏去折腾,换张表就崩。

今天老汪就教大家用 Power Query 的 M 语言思维,只用一个极简的核心函数 List.Distinct,在几秒钟内自由切换这两种排名!


一、 为什么老汪说“排名的本质就是列表比较”?

很多朋友一听 M 语言就觉得高深莫测,其实它底层的逻辑非常直白朴素。大家不用去死记硬背复杂的公式,听老汪把这个数学逻辑拆解给你听:

所谓“我排第几名”,本质上就是问一句话:“全厂有几个产线的效益严格比我高?”

  1. 国际跳跃排名(跳名次):
    – 规则:看看全厂效益列表中,有几个数字比我大,然后加 1。
    – 比如有两条产线并列 3200。排到 2850 的产线时,比它大的数字有两个 3200,计数为 2,加 1 之后自然就变成了第 3 名(这就是 1, 1, 3 的由来)。
    – M 语言算法:List.Count(List.Select(全厂列表, each _ > 我的效益)) + 1

  2. 中国式连续排名(不跳名次,稠密排名):
    – 规则:看看全厂比我大的“不重复”效益数值有几个,然后加 1。
    – 同样是并列的两个 3200,在统计前我如果先给它们去个重,两个 3200 瞬间就缩减成了 1 个数字!比我大的唯一数值只有 1 个,计数为 1,加 1 之后紧接着就是第 2 名(这就是 1, 1, 2 的由来)!
    – M 语言算法:List.Count(List.Distinct(List.Select(全厂列表, each _ > 我的效益))) + 1

大家看,是不是就差了一个 List.Distinct?
仅靠这一个函数去重,四两拨千斤,中国式连续排名瞬间搞定!


二、 走进车间:汽车零部件真实案例现场演示

拿老汪经手的一家知名汽车底盘与动力总成零部件制造企业(Tier 1 一级供应商)为例。厂里有精密压铸、CNC数控机加、冲压焊接三个车间,共 8 条主要生产线,生产供应不同主机厂车型的关键部件。

月末老汪从 MES 和 ERP 系统里导出了各产线的实际运营数据(大家可以下载文末的随堂课件跟着老汪一起动手):

零件编码 车间名称 生产产线 核心配套部件 月度完工量(套) 单位边际贡献(元/套) 业务背景与看点
P-812-ARM 精密压铸一车间 压铸自动化1号线 铝合金底盘下摆臂 4500 3200 明星件,并列第 1
P-501-CAL CNC机加二车间 精密机加柔性线 双活塞制动卡钳总成 6000 3200 效益齐平,并列第 1
P-245-DSK 精密压铸一车间 重力铸造2号线 轻量化通风制动盘 5200 2850 并列第二档效益
P-692-KNL CNC机加二车间 立式加工中心3号线 高强度前轮转向节 3800 2850 并列第二档效益
P-207-TRK 冲压焊接三车间 机器人焊接1号线 前悬架减震器支架 4800 2600 骨干稳产线
P-ECH-SHA CNC机加二车间 数控车削磨削线 驱动半轴总成 4100 2400 中等水平
P-XY0-TRAY 冲压焊接三车间 大型伺服冲压线 电池包下壳体托盘 3500 1950 新能源大件,并列待降本
P-GAS-EXT 冲压焊接三车间 激光切割焊接线 排气隔热消音总成 5000 1950 传统件,并列待降本

大家看,这里面有两组并列第一(3200)、两组并列(2850)、两组并列垫底(1950),是非常具有代表性的汽车零部件制造真实数据。


三、 老汪手把手带你在高级编辑器中实操

💡 老汪敲黑板(免改路径直接刷新秘籍):
平时做财务分析或者发课件给同事,最怕什么?最怕换台电脑一刷新就蹦出一堆刺眼的红色报错:“找不到文件路径 C:\Users…”,场面别提多尴尬了!
所以老汪在给你们配套的随堂 Excel 课件里,特意准备了一个“偷懒防翻车秘籍”:
老汪在【配置】表的 A1 单元格写好了一个自适应公式:=LEFT(CELL("filename",$A$1), FIND("[", CELL("filename",$A$1))-1),并且把它定义为名称【当前路径】。
这样不管你把课件解压到 D 盘、E 盘、桌面还是 U 盘,甚至发给车间的同事,打开直接点击【全部刷新】,PQ 会自动感知当前文件夹位置,绝不会报找不到路径的红字!大家学完后,强烈建议把这招抄到你们自己公司的模板里去,能少加很多冤枉班!

大家打开 Power Query 编辑器,新建一个空查询,打开【高级编辑器】,直接看老汪写的这段生产级代码:

let
    // 1. 【动态相对路径】:读取老汪预设的当前工作簿目录,换电脑绝不报错
    当前路径 = try 
        let 
            raw = Text.From(Excel.CurrentWorkbook(){[Name="当前路径"]}[Content]{0}[Column1]),
            p = Text.Trim(raw)
        in 
            if Text.EndsWith(p, "\") then p else p & "\"
    otherwise 
        "./", // 兜底相对路径

    数据源文件 = 当前路径 & "原始业务数据表.xlsx",

    // 2. 加载工作簿并读取「产线效益表」
    工作簿 = Excel.Workbook(File.Contents(数据源文件), null, true),
    产线表 = 工作簿{[Item="产线效益表", Kind="Sheet"]}[Data],
    提升标题 = Table.PromoteHeaders(产线表, [PromoteAllScalars = true]),
    更改类型 = Table.TransformColumnTypes(提升标题, {
        {"零件编码", type text}, {"车间名称", type text}, {"生产产线", type text},
        {"核心配套部件", type text}, {"月度完工量(套)", Int64.Type}, {"单位边际贡献(元/套)", type number}
    }),

    // 3. 【老汪核心技巧】:把全厂产线的边际贡献单独提成一个纯净列表
    全厂边际贡献列表 = 更改类型[#"单位边际贡献(元/套)"],

    // 4. 【算国际跳跃排名】:直接比大小并计数 + 1
    添加国际排名 = Table.AddColumn(更改类型, "国际排名(跳跃)", each 
        let 
            当前值 = [#"单位边际贡献(元/套)"],
            大于当前值的列表 = List.Select(全厂边际贡献列表, each _ > 当前值),
            排名 = List.Count(大于当前值的列表) + 1
        in 
            排名, 
        Int64.Type
    ),

    // 5. 【算中国稠密排名】:在计数前用 List.Distinct 巧妙去重 + 1
    添加中国排名 = Table.AddColumn(添加国际排名, "中国排名(连续)", each 
        let 
            当前值 = [#"单位边际贡献(元/套)"],
            大于当前值的列表 = List.Select(全厂边际贡献列表, each _ > 当前值),
            去重后的列表 = List.Distinct(大于当前值的列表), // 关键一步:List.Distinct 抹平重复占位!
            排名 = List.Count(去重后的列表) + 1
        in 
            排名, 
        Int64.Type
    ),

    // 6. 按边际贡献降序排列,清晰呈现给管理层
    按效益降序 = Table.Sort(添加中国排名, {{"单位边际贡献(元/套)", Order.Descending}})
in
    按效益降序

四、 最终清洗效果与管理层看板对照

运行完成后,出来的结果表格是这样的:

零件编码 车间名称 生产产线 核心部件 单位边际贡献 国际排名(跳跃) 中国排名(连续) 老汪的业务点评
P-812-ARM 精密压铸一车间 压铸自动化1号线 铝合金下摆臂 3200 1 1 并列冠亚军
P-501-CAL CNC机加二车间 精密机加柔性线 制动卡钳总成 3200 1 1 并列冠亚军
P-245-DSK 精密压铸一车间 重力铸造2号线 通风制动盘 2850 3 ⚠️直接跳3 2 ✅紧接2名 连续排名避免车间主任掀桌子
P-692-KNL CNC机加二车间 立式加工中心3号线 前轮转向节 2850 3 2 并列第2档
P-207-TRK 冲压焊接三车间 机器人焊接1号线 减震器支架 2600 5 ⚠️跳过4 3 ✅紧接3名 阶梯式等级清晰直观
P-ECH-SHA CNC机加二车间 数控车削磨削线 驱动半轴总成 2400 6 4 稳步递推
P-XY0-TRAY 冲压焊接三车间 大型伺服冲压线 电池包下壳体 1950 7 5 并列待降本
P-GAS-EXT 冲压焊接三车间 激光切割焊接线 排气隔热消音 1950 7 5 并列待降本

大家看,右边的“中国排名”一目了然:第 1 档、第 2 档、第 3 档、第 4 档、第 5 档,等级严谨清晰,拿去给车间发月度绩效,谁也挑不出毛病!


五、 老汪从业多年总结的 2 个经典避坑指南

  1. 下划线 _ 的作用域问题(千万别弄混):
    很多朋友经常向老汪提问:“老汪,为什么我在 List.Select 里面写 each _ > [单位边际贡献] 会报错?”
    老汪提醒大家:在 Table.AddColumn 里的 _ 代表的是当前这一整行;而在里面的 List.Select 里的 _ 代表的是列表里的单个数字。两层 _ 碰在一起,内层会把外层吃掉!
    所以大家看老汪的代码:在调用 List.Select 之前,我用 let 当前值 = [#"单位边际贡献(元/套)"] in ... 先把当前行的数字抓出来存进变量,再去里面比,清清爽爽,绝对不出错!

  2. 面对可能停产的 null 空值:
    汽车零部件车间里遇到模具改模或者主机厂产线切换,有些月份某个零件是没有排产数据的(字段是 null)。大家在比大小之前,可以在列表上套一个 List.RemoveNulls 把空值滤掉,防止数字和 null 比较引发异常。


好了,今天这节课就跟大家聊到这里。大家光看不练等于没学,老汪已经把本节课涉及的原始练手数据表、做好的自适应完成表以及 M 脚本源码完整打包好了。大家直接点击下方醒目的卡片免费下载回去实际演练一遍。如果在实操中遇到任何问题,欢迎在评论区随时给我留言,老汪看到都会一一解答!

📦

本节实操随堂 Excel 课件包免费下载

包含配套的《原始业务数据表.xlsx》(练手用)与《最终完成版_带PQ查询.xlsx》(预置当前路径自适应公式,下载解压即可直接刷新),以及纯文本 M 语言代码文件。

文件:EP016_List_Distinct_汽车零部件产线绩效排名_随堂课件.zip | 大小:12.9 KB | 格式:.zip 压缩包


📥 立即免费下载本节课件包 (.zip)

© 版权声明
THE END
喜欢就支持一下吧
点赞15 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容