订阅更新

数据膨胀与JOIN陷阱

数仓建模手册 第 7 题 约 4 分钟读完

本文目录
  1. 总结
  2. 数据膨胀:数仓开发的隐形杀手
  3. 最常见的三种膨胀场景
  4. 防膨胀排查四步法

总结

记忆点内容
膨胀根因JOIN键不唯一,多对多关联
三大场景维度表主键重复、粒度不对齐、多对多JOIN
排查四步查唯一性 → 对比行数 → 定位Key → 确认粒度
预防手段ETL加行数校验卡点,膨胀自动告警
核心原则每次JOIN前先确认粒度和主键唯一性

先把这段讲顺;面试官追问时,再展开下面的原理和细节。

详细讲解

进阶 | 大数据面试 · 数仓建模系列

面试真题
你有没有遇到过JOIN之后数据行数变多的情况?是什么原因?怎么排查和解决的?

数据膨胀:数仓开发的隐形杀手

这个问题看起来基础,但我敢说80%的数据开发都踩过这个坑,包括我自己。场景还原:你写了一个简单的JOIN,原表A有100万行,JOIN之后变成了150万行。金额加起来多了50%,报表数字全是错的。

根因只有一个:JOIN键不唯一,产生了多对多关联。

最常见的三种膨胀场景

场景一:维度表主键不唯一

最经典的坑。你以为dim_user的user_id是唯一的,但实际上:拉链表忘了加is_current = 1过滤;数据源有重复(上游没去重);全量和增量混跑,同一个ID有多条记录。

Wood的实战经验
我之前遇到过一次:用户维度表的is_current标记有Bug,同一个user_id有两条current=1的记录,JOIN之后订单行数翻倍,GMV直接多算了一倍。当天报表就被业务方投诉了。
从那以后,我在维度表的ETL任务里加了主键唯一性校验——不通过就不写入下游。这一个小改动,避免了后续无数次类似的事故。

场景二:粒度不对齐

事实表粒度是「订单+商品」(一个订单3件商品=3行),你JOIN了一张「订单粒度」的优惠券表(一个订单1行),看起来没问题。但如果反过来,订单粒度的事实表JOIN了商品粒度的价格表,一个订单的3件商品各关联一条价格记录,就膨胀了。核心原则:JOIN之前先确认两边的粒度是否对齐。

场景三:多对多JOIN

最危险的情况。比如一个用户属于多个用户组、一个商品有多个标签、一个订单用了多张优惠券。本来两个订单300元,JOIN之后变成4行600元。如果你没发现,SUM(amount)直接错一倍。

防膨胀排查四步法

我总结的一套排查流程,写SQL前过一遍能避免90%的膨胀问题:

第一步:检查JOIN键唯一性。用GROUP BY join_key HAVING COUNT(*) > 1分别检查两侧。

第二步:对比JOIN前后行数。JOIN前COUNT(*)和JOIN后COUNT(*)对比,如果增长了就必有膨胀。

第三步:定位膨胀的Key。找到具体哪些Key导致了行数增加。

第四步:确认粒度对齐。画一张简单的表格:A表一行=什么,B表一行=什么,JOIN关系是一对一还是一对多?

-- 检查维度表主键唯一性
SELECT user_id, COUNT(*) AS cnt
FROM dim_user WHERE is_current = 1
GROUP BY user_id HAVING cnt > 1;
-- 有结果说明主键不唯一,JOIN必膨胀
-- 对比JOIN前后行数
SELECT COUNT(*) FROM fact_order; -- 比如1000000
SELECT COUNT(*) FROM fact_order a
JOIN dim_user b ON a.user_id = b.user_id;
-- 如果 > 1000000,必有膨胀
面试加分提示
这题面试官想考的是你的数据质量意识和排查能力。高分回答要有具体案例:
「我遇到过一次,用户宽表的金额指标比源表多了将近一倍。排查后发现是用户维度表的拉链表没加is_current过滤,user_id有两条有效记录,JOIN导致行数翻倍。修复后我在ETL流程里加了一个质量卡点:每次JOIN之后自动校验行数,如果增长超过1%就告警,避免这类问题再次漏到下游。」
关键词:发现问题 → 定位原因 → 修复 → 预防机制。有闭环的回答才是好回答。
刷题进度保存在当前浏览器