一次目标值对不上的 ETL 排查实战:从“怀疑重复”到定位“负责人归属口径不一致”

5 阅读12分钟

# 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), 7AS `月份`,  `站点`,  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), 7AS `月份`,    `站点`,    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), 7AS `月份`,    `站点`,    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_目标销量`, 0AS `目标销量差异`,  COALESCE(f.`final_目标销售额`, 0- COALESCE(t.`target_目标销售额`, 0AS `目标销售额差异`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.`目标总销量`, 0DESC,        COALESCE(input1.`目标总销售额`, 0DESC    ) 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脱敏人名乙脱敏人名甲10428.767123
脱敏asin2脱敏人名乙脱敏人名甲301197.260274
脱敏asin3脱敏人名乙脱敏人名甲753516.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 排查时最容易踩的坑之一:

指标值正确,不代表切片结果正确;切片结果不对,往往要从维度来源和口径开始查。