# 20260427一次目标值对不上的 ETL 排查实战:从“怀疑重复”到定位“负责人归属口径不一致”
最近排查了一个经营分析类 ETL 的问题,表面现象看起来很简单:
同样是查 2026-04、CA 站点、运营负责人=脱敏人名甲 的目标值,两个数据集查出来的结果却对不上。
一个来自目标表:
- •
运营报表-25年Q3和Q4目标数据
一个来自最终产出表:
- •
经营日报应用-月报-asin
最开始我以为只是简单的重复数据或者聚合问题,但一路往下排,最后发现这个问题其实分成了两个层次:
这篇文章就完整记录一下这次 debug 的过程。
一、问题现象
先看最开始发现的问题。
1)目标表中,脱敏人名甲 的目标值
SELECT SUM(`目标总销量`) AS `月度目标销量`, SUM(`目标总销售额`) AS `月度目标销售额`, MIN(`币别`) AS `币别`FROM input1WHERE `运营负责人` = '脱敏人名甲' AND LEFT(CAST(`月份` AS STRING), 7) = '2026-04' AND `站点` = 'CA';
结果:
- • 月度目标销量:
4631 - • 月度目标销售额:
116008.219178 - • 币别:
CAD
2)最终月报中,脱敏人名甲 的目标值
SELECT SUM(`月度目标销量`) AS `月度目标销量`, SUM(`月度目标销售额`) AS `月度目标销售额`, MIN(`币别`) AS `币别`FROM input1WHERE `运营负责人` = '脱敏人名甲' AND LEFT(CAST(`月份` AS STRING), 7) = '2026-04' AND `站点` = 'CA';
结果:
- • 月度目标销量:
4746 - • 月度目标销售额:
121150.684932 - • 币别:
CAD
差异为:
- • 目标销量差异:
115 - • 目标销售额差异:
5142.465754
币别一致,但值不一致。
二、第一反应:是不是目标表重复了?
因为最终月报里的目标值,本质上是从目标表关联进来的,所以我的第一反应是:
会不会目标表存在重复行,导致 join 之后被放大?
于是先查目标表中是否存在 月份 + 站点 + ASIN 维度重复。
SELECT LEFT(CAST(`月份` AS STRING), 7) AS `月份`, `站点`, UPPER(TRIM(`ASIN`)) AS `ASIN`, COUNT(*) AS cnt, CONCAT_WS(',', COLLECT_SET(`运营负责人`)) AS 目标表运营负责人集合, SUM(COALESCE(`目标总销量`, 0)) AS sum_目标总销量, SUM(COALESCE(`目标总销售额`, 0)) AS sum_目标总销售额FROM `运营报表-25年Q3和Q4目标数据`WHERE LEFT(CAST(`月份` AS STRING), 7) = '2026-04' AND `站点` = 'CA'GROUP BY LEFT(CAST(`月份` AS STRING), 7), `站点`, UPPER(TRIM(`ASIN`))HAVING COUNT(*) > 1ORDER BY cnt DESC;
结果果然查到了重复,例如:
- •
脱敏asin1 - •
脱敏asin2 - •
脱敏asin3
而且这些重复行里,很典型的是:
- • 一条有目标值
- • 一条目标值为 0
例如:
- • 同一个 ASIN,有一条
目标总销量=100,目标总销售额=2099 - • 另一条是
目标总销量=0,目标总销售额=0
这时候如果后续 ETL 直接按 月份 + 站点 + ASIN 去 join,就很容易把目标值放大。
三、验证:是不是重复导致的翻倍?
为了验证这个猜想,我把目标表和最终月报按 ASIN 逐个比对。
WITH f AS ( SELECT LEFT(CAST(`月份` AS STRING), 7) AS `月份`, `站点`, UPPER(TRIM(`ASIN`)) AS asin_key, `ASIN`, `运营负责人` AS `final_运营负责人`, SUM(COALESCE(`月度目标销量`, 0)) AS `final_目标销量`, SUM(COALESCE(`月度目标销售额`, 0)) AS `final_目标销售额` FROM input1 WHERE LEFT(CAST(`月份` AS STRING), 7) = '2026-04' AND `站点` = 'CA' GROUP BY LEFT(CAST(`月份` AS STRING), 7), `站点`, UPPER(TRIM(`ASIN`)), `ASIN`, `运营负责人`),t AS ( SELECT LEFT(CAST(`月份` AS STRING), 7) AS `月份`, `站点`, UPPER(TRIM(`ASIN`)) AS asin_key, `ASIN`, `运营负责人` AS `target_运营负责人`, SUM(COALESCE(`目标总销量`, 0)) AS `target_目标销量`, SUM(COALESCE(`目标总销售额`, 0)) AS `target_目标销售额` FROM input2 WHERE LEFT(CAST(`月份` AS STRING), 7) = '2026-04' AND `站点` = 'CA' GROUP BY LEFT(CAST(`月份` AS STRING), 7), `站点`, UPPER(TRIM(`ASIN`)), `ASIN`, `运营负责人`)SELECT COALESCE(f.`月份`, t.`月份`) AS `月份`, COALESCE(f.`站点`, t.`站点`) AS `站点`, COALESCE(f.asin_key, t.asin_key) AS asin_key, f.`final_运营负责人`, t.`target_运营负责人`, f.`final_目标销量`, t.`target_目标销量`, f.`final_目标销售额`, t.`target_目标销售额`, COALESCE(f.`final_目标销量`, 0) - COALESCE(t.`target_目标销量`, 0) AS `目标销量差异`, COALESCE(f.`final_目标销售额`, 0) - COALESCE(t.`target_目标销售额`, 0) AS `目标销售额差异`FROM fFULL OUTER JOIN t ON f.`月份` = t.`月份` AND f.`站点` = t.`站点` AND f.asin_key = t.asin_keyWHERE f.`final_运营负责人` LIKE '%脱敏人名甲%' OR t.`target_运营负责人` = '脱敏人名甲'ORDER BY ABS(COALESCE(f.`final_目标销售额`, 0) - COALESCE(t.`target_目标销售额`, 0)) DESC;
当时查出来像这样:
- •
脱敏asin1:目标表 600,最终月报 1200 - •
脱敏asin2:目标表 280,最终月报 560 - •
脱敏asin3:目标表 100,最终月报 200
也就是刚好翻倍。
这一步已经可以确认:
目标表重复确实会导致最终月报目标值被放大。
四、第一阶段修复:在目标表里先做去重
因为问题源头就在 运营报表-25年Q3和Q4目标数据,所以最终我没有在下游 ETL 暴力补丁,而是直接在这个目标数据集里加了窗口函数去重。
去重逻辑是:
- • 按
月份 + 站点 + ASIN分组 - • 同组里优先保留
目标总销量更大的 - • 如果销量相同,再保留
目标总销售额更大的 - • 最后只取
rn = 1
SQL 如下:
WITH ranked_target AS ( SELECT input1.`月份`, input1.`站点`, input1.`ASIN`, input1.`目标总销量`, input1.`目标总销售额`, input1.`目标总毛利额`, input1.`目标总毛利率`, input1.`站内广告预算`, input1.`回款额`, input1.`运营负责人`, input1.`部门`, input1.`币别`, ROW_NUMBER() OVER ( PARTITION BY DATE_FORMAT(input1.`月份`, 'yyyy-MM'), TRIM(input1.`站点`), UPPER(TRIM(input1.`ASIN`)) ORDER BY COALESCE(input1.`目标总销量`, 0) DESC, COALESCE(input1.`目标总销售额`, 0) DESC ) AS rn FROM input1)SELECT `月份`, `站点`, `ASIN`, `目标总销量`, `目标总销售额`, `目标总毛利额`, `目标总毛利率`, `站内广告预算`, `回款额`, `运营负责人`, `部门`, `币别`FROM ranked_targetWHERE rn = 1;
去重后,再跑一遍重复检查:
SELECT input1.`月份`, input1.`站点`, input1.`ASIN`, COUNT(*)FROM input1GROUP BY input1.`月份`, input1.`站点`, input1.`ASIN`HAVING COUNT(*) > 1;
结果已经没有任何异常数据。
五、去重后,为什么问题还“没完全解决”?
目标表去重后,我继续做了一步非常关键的验证。
1)不按负责人过滤,只看 2026-04 + CA 的总目标
目标表:
SELECT SUM(COALESCE(`目标总销量`, 0)) AS `目标表_目标销量`, SUM(COALESCE(`目标总销售额`, 0)) AS `目标表_目标销售额`FROM `运营报表-25年Q3和Q4目标数据`WHERE LEFT(CAST(`月份` AS STRING), 7) = '2026-04' AND `站点` = 'CA';
最终月报:
SELECT SUM(COALESCE(`月度目标销量`, 0)) AS `月报_目标销量`, SUM(COALESCE(`月度目标销售额`, 0)) AS `月报_目标销售额`FROM `经营日报应用-月报-asin`WHERE LEFT(CAST(`月份` AS STRING), 7) = '2026-04' AND `站点` = 'CA';
结果两边完全一致:
- • 目标销量:
20898 - • 目标销售额:
618735.616438
这一步非常重要,因为它说明:
目标值总量已经没问题了。
也就是说:
- • 不是最终月报还在重复放大
- • 不是 join 还有问题
- • 不是目标值丢失或多算
那为什么按“脱敏人名甲”过滤还对不上?
答案是:
负责人口径不一致。
六、第二阶段定位:同一个 ASIN 的负责人,在两个表里不一样
于是我继续查:
- • 目标表里的
运营负责人 - • 最终月报里的
运营负责人
是不是同一个人。
我把条件改成:
- • 目标表负责人不是 脱敏人名甲
- • 但最终月报负责人是 脱敏人名甲
结果查出来正好 3 个 ASIN:
| ASIN | 目标表运营负责人 | 最终月报运营负责人 | 目标总销量 | 目标总销售额 |
|---|---|---|---|---|
| 脱敏asin1 | 脱敏人名乙 | 脱敏人名甲 | 10 | 428.767123 |
| 脱敏asin2 | 脱敏人名乙 | 脱敏人名甲 | 30 | 1197.260274 |
| 脱敏asin3 | 脱敏人名乙 | 脱敏人名甲 | 75 | 3516.438356 |
这三条加起来刚好就是差异:
- • 销量差异:
10 + 30 + 75 = 115 - • 销售额差异:
428.767123 + 1197.260274 + 3516.438356 = 5142.465753
与前面两次查询的差异完全一致。
到这里,问题已经彻底清楚了:
目标值没有问题,
真正的问题是:同一个 ASIN 在目标表和最终月报里的负责人归属不一致。
七、为什么负责人会不一致?问题很可能从 经营日报应用-日报-asin 就开始了
最终月报里的 运营负责人 不是从目标表来的,而是从实际经营链路来的:
经营日报应用-日报-msku→ 经营日报应用-日报-asin→ 经营日报应用-月报-asin
而在 经营日报应用-日报-asin 里,负责人是这样聚合的:
CASE WHEN SUM(CASE WHEN `MSKU状态` != '停售' THEN 1 ELSE 0 END) > 0 THEN ARRAY_JOIN( FILTER( ARRAY_AGG(DISTINCT CASE WHEN `MSKU状态` != '停售' THEN `运营负责人` ELSE NULL END), x -> x IS NOT NULL ), ',' ) ELSE ARRAY_JOIN(FILTER(ARRAY_AGG(DISTINCT `运营负责人`), x -> x IS NOT NULL), ',')END AS `运营负责人`
这段逻辑的意思是:
- • 如果 ASIN 下存在非停售 MSKU
- • 那么只取非停售 MSKU 的负责人
- • 停售 MSKU 的负责人不参与
所以一个 ASIN 在目标表里属于“脱敏人名乙”,但如果这个 ASIN 在实际经营数据里:
- • 脱敏人名乙对应的 MSKU 已经停售
- • 脱敏人名甲负责的 MSKU 仍然在售
那么日报-asin 里聚出来的负责人就会是 脱敏人名甲。
这其实不是 bug,而是口径使然。
八、月报里还有第二层不稳定因素:ANY_VALUE(运营负责人)
到了 经营日报应用-月报-asin 的月聚合阶段,又用了这一句:
ANY_VALUE(input1.`运营负责人`) AS `运营负责人`
而月报的聚合粒度是:
- •
月份 + 站点 + ASIN
这意味着:
- • 如果同一个 ASIN 在月内不同日期负责人不完全一致
- • 那么最后月报里取到哪个负责人,是一个不稳定值
也就是说,最终月报的 运营负责人 有两个问题:
九、最终结论
这次问题分成两个阶段。
第一阶段问题:目标表重复
根因:
- •
运营报表-25年Q3和Q4目标数据存在月份 + 站点 + ASIN重复 - • 下游关联时导致目标值翻倍
修复:
- • 在目标表中使用
ROW_NUMBER()去重 - • 保留
目标总销量、目标总销售额更大的那一条
第二阶段问题:负责人口径不一致
根因:
- • 目标表里的
运营负责人是目标归属负责人 - • 最终月报里的
运营负责人是实际经营负责人 - • 实际经营负责人在
日报-asin里受“非停售 MSKU”规则影响 - • 在
月报-asin又经过了ANY_VALUE
结果:
- • 总目标值一致
- • 但按负责人过滤时不一致
十、最终解决建议
方案一:最推荐
在最终月报中保留两个负责人字段:
- •
运营负责人:实际经营负责人 - •
目标运营负责人:目标表负责人
这样以后:
- • 查目标值,用
目标运营负责人 - • 查实际经营指标,用
运营负责人
这是最清晰、最不容易混淆的方案。
方案二:如果只能保留一个负责人字段
那就必须先明确你要的业务语义:
- • 如果要“目标口径一致”,就用目标表负责人
- • 如果要“实际经营口径一致”,就用日报/月报链路负责人
不能一边用实际负责人,一边又希望目标值按目标负责人完全对齐。
方案三:优化月报负责人逻辑
ANY_VALUE(运营负责人) 不够稳定,建议改成更明确的规则,例如:
- • 取月末最后一天负责人
- • 取当月销量最大的负责人
- • 或直接保留负责人集合,不做任意取值
十一、这次排查的经验总结
这次问题最值得记住的,不是某一条 SQL,而是几个排查思路。
1)先看总量,再看切片
一开始我直接按负责人查,容易陷入“到底是目标值错了,还是负责人错了”的混乱。
后来先对比总量,马上就知道:
- • 总量一致 → 指标没错
- • 切片不一致 → 维度归属有问题
这一步非常关键。
2)不要只怀疑指标,维度同样会出错
很多 ETL 问题表面看起来是“数不对”,本质上其实是“维度归属口径不同”。
3)窗口函数去重比下游补丁更干净
重复问题最好在源头解决,而不是在下游报表层临时兜底。
4)ANY_VALUE 很危险
只要字段不是天然单值,ANY_VALUE 就可能埋下不稳定隐患。
十二、结语
这次问题表面上是:
“为什么同样筛选 脱敏人名甲,目标值对不上?”
最后真正定位到的是:
“目标值已经对上了,只是两个表里的‘运营负责人’根本不是同一个口径。”
前半段是典型的数据去重问题,后半段则是典型的维度归属问题。
如果不把这两层拆开,很容易一直在错误方向上调 SQL。
这也是做 ETL 排查时最容易踩的坑之一:
指标值正确,不代表切片结果正确;切片结果不对,往往要从维度来源和口径开始查。