文 / 老汪(汽车零部件财务与商业智能实战派)
大家好,我是老汪。
在咱们汽车零部件制造企业(Tier 1 一级供应商)月末开月度经营分析会的时候,做财务 BP、成本会计或者运营分析的朋友,经常会遇到一个让人头疼的场景:
车间主任、生产副总和厂长们坐在一起,桌上摆着各个车间(精密压铸、数控机加、冲压焊接)各条产线的完工量和单位边际贡献(单价减去单位变动成本)。为了衡量各条产线承接主机厂(比亚迪、吉利、特斯拉等)车型的盈利能力,财务需要给每条产线排个座次、核发月度绩效奖金。
但在汽车零部件实际生产中,经常会出现几条产线的单位边际贡献刚好一模一样的情况(比如压铸一线的铝合金下摆臂,和机加二线的双活塞制动卡钳总成,单位边际贡献都是 3200 元/套,并列第一)。
这时候考核委员会的争议就来了:
– 如果按照国际通用的跳跃排名(Rank):并列第 1 名之后,下一条产线直接跳到了第 3 名(中间第 2 名空缺)。机加车间主任立马不干了:“老汪,我们明明只比第一名差一点点,怎么直接掉到第三名去了?第二名被狗吃了?”
– 但如果按咱们中国本土习惯的“连续稠密排名”(Dense Rank):并列第 1 名之后,下一条产线紧接着算作第 2 名(名次不空,按阶梯拿奖),大家心服口服。
很多财务朋友在 Excel 里写了极其复杂的 COUNTIF 加条件嵌套,甚至用 VBA 宏去折腾,换张表就崩。
今天老汪就教大家用 Power Query 的 M 语言思维,只用一个极简的核心函数 List.Distinct,在几秒钟内自由切换这两种排名!
一、 为什么老汪说“排名的本质就是列表比较”?
很多朋友一听 M 语言就觉得高深莫测,其实它底层的逻辑非常直白朴素。大家不用去死记硬背复杂的公式,听老汪把这个数学逻辑拆解给你听:
所谓“我排第几名”,本质上就是问一句话:“全厂有几个产线的效益严格比我高?”
-
国际跳跃排名(跳名次):
– 规则:看看全厂效益列表中,有几个数字比我大,然后加 1。
– 比如有两条产线并列 3200。排到 2850 的产线时,比它大的数字有两个 3200,计数为 2,加 1 之后自然就变成了第 3 名(这就是 1, 1, 3 的由来)。
– M 语言算法:List.Count(List.Select(全厂列表, each _ > 我的效益)) + 1 -
中国式连续排名(不跳名次,稠密排名):
– 规则:看看全厂比我大的“不重复”效益数值有几个,然后加 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 个经典避坑指南
-
下划线
_的作用域问题(千万别弄混):
很多朋友经常向老汪提问:“老汪,为什么我在List.Select里面写each _ > [单位边际贡献]会报错?”
老汪提醒大家:在Table.AddColumn里的_代表的是当前这一整行;而在里面的List.Select里的_代表的是列表里的单个数字。两层_碰在一起,内层会把外层吃掉!
所以大家看老汪的代码:在调用List.Select之前,我用let 当前值 = [#"单位边际贡献(元/套)"] in ...先把当前行的数字抓出来存进变量,再去里面比,清清爽爽,绝对不出错! -
面对可能停产的
null空值:
汽车零部件车间里遇到模具改模或者主机厂产线切换,有些月份某个零件是没有排产数据的(字段是null)。大家在比大小之前,可以在列表上套一个List.RemoveNulls把空值滤掉,防止数字和 null 比较引发异常。
好了,今天这节课就跟大家聊到这里。大家光看不练等于没学,老汪已经把本节课涉及的原始练手数据表、做好的自适应完成表以及 M 脚本源码完整打包好了。大家直接点击下方醒目的卡片免费下载回去实际演练一遍。如果在实操中遇到任何问题,欢迎在评论区随时给我留言,老汪看到都会一一解答!
本节实操随堂 Excel 课件包免费下载
包含配套的《原始业务数据表.xlsx》(练手用)与《最终完成版_带PQ查询.xlsx》(预置当前路径自适应公式,下载解压即可直接刷新),以及纯文本 M 语言代码文件。














暂无评论内容