4、数据转换核心:数据清洗、标准化、脱敏与聚合

数据转换这个环节,说白了就是 ELT 流程里最费功夫、也最见功力的部分。我见过太多团队,数据从源端抽过来了,加载也完成了,结果在转换这一步翻车——要么跑出来的报表对不上,要么跑着跑着任务就挂了。

这一章,咱们就把转换环节的四个核心动作拆开揉碎了讲。你想想看,数据进了仓库,第一件事是什么?

4.1 数据清洗:去重与空值处理

数据清洗是转换的第一步,也是我每次做 ELT 项目时盯得最紧的一环。脏数据进来,后面全是白费功夫。

4.1.1 去重:别让重复数据毁了你的报表

去重这件事,听起来简单,做起来坑不少。我个人习惯用 ROW_NUMBER() 配合窗口函数来处理,而不是简单的 DISTINCT。

核心原则:去重前先搞清楚业务上的唯一键是什么,别盲目删数据。

-- 按业务主键去重,保留最新一条记录
WITH dedup AS (
  SELECT 
    *,
    ROW_NUMBER() OVER (
      PARTITION BY user_id, order_date
      ORDER BY load_timestamp DESC
    ) AS rn
  FROM raw_orders
)
SELECT * FROM dedup WHERE rn = 1;

我的经验:我曾经在一个电商项目中,直接用 DISTINCT 去重,结果把同一用户同一天的多笔订单给合并了。财务对账时发现金额对不上,查了两天才找到原因。从那以后,我坚持用窗口函数 + 业务主键的方式去重。

4.1.2 空值处理:别让 NULL 悄悄搞破坏

空值处理,我见过三种主流策略:

  • 直接丢弃:对于关键字段为空的记录,直接过滤掉。适合对数据完整性要求极高的场景。
  • 填充默认值:用 COALESCE 或 IFNULL 填充业务默认值。比如年龄字段为空,填 0 或平均值。
  • 标记保留:用特殊标记(如 -999、'UNKNOWN')保留空值记录,方便后续排查。
-- 空值填充示例
SELECT 
  user_id,
  COALESCE(age, 0) AS age,
  COALESCE(email, 'unknown@email.com') AS email,
  CASE 
    WHEN phone IS NULL THEN 'missing_phone'
    ELSE phone 
  END AS phone_status
FROM raw_users;

避坑指南:我曾经在处理用户画像数据时,把年龄空值全填了 0。结果做用户分群时,0 岁用户成了最大群体,业务方直接懵了。后来我改成用中位数填充,效果好了很多。记住:填充策略一定要结合业务场景。

4.2 数据标准化:让数据说同一种语言

数据标准化,说白了就是把不同来源的数据统一成同一套规范。我做过一个跨国项目,同一个日期字段,有的源用 '2024-01-15',有的用 '01/15/2024',还有用 '15-JAN-2024' 的。不标准化,后面根本没法做聚合。

标准化类型 常见问题 推荐做法
日期格式 多种格式混用 统一转为 ISO 8601 (YYYY-MM-DD)
货币单位 不同币种、不同精度 统一为基准币种,保留两位小数
编码规范 性别、状态等字段编码不一致 建立映射表,统一编码规则
文本大小写 大小写混用导致分组异常 统一转为小写或首字母大写
-- 日期标准化示例
SELECT 
  order_id,
  CASE 
    WHEN order_date LIKE '%/%' THEN 
      TO_DATE(order_date, 'MM/DD/YYYY')
    WHEN order_date LIKE '%-%' THEN 
      TO_DATE(order_date, 'YYYY-MM-DD')
    ELSE 
      TO_DATE(order_date, 'DD-MON-YYYY')
  END AS standardized_date
FROM raw_orders;

我的习惯:我会在 ELT 流程的早期阶段就建立一套标准化映射表,把各个源系统的编码规则、格式规范都记录下来。这样后续处理时直接查表转换,既高效又不容易出错。

4.3 数据脱敏:保护敏感信息

数据脱敏这件事,现在越来越重要了。GDPR、个人信息保护法,哪个都不是闹着玩的。我在金融行业做项目时,脱敏是必须过的关卡。

常用的脱敏方法有这几种:

  • 替换:用固定字符替换敏感字段。比如手机号中间四位用 **** 代替。
  • 遮蔽:只显示部分信息。比如身份证号只显示前六位和后四位。
  • 哈希:用哈希算法对敏感信息进行不可逆转换。适合需要保持唯一性的场景。
  • 泛化:降低数据精度。比如把具体年龄变成年龄段。
-- 手机号脱敏示例
SELECT 
  user_id,
  CONCAT(
    SUBSTRING(phone, 1, 3),
    '****',
    SUBSTRING(phone, 8, 4)
  ) AS masked_phone,
  -- 哈希脱敏(保持唯一性)
  MD5(email) AS hashed_email,
  -- 泛化年龄
  CASE 
    WHEN age BETWEEN 0 AND 18 THEN '0-18'
    WHEN age BETWEEN 19 AND 35 THEN '19-35'
    WHEN age BETWEEN 36 AND 60 THEN '36-60'
    ELSE '60+'
  END AS age_group
FROM raw_users;

避坑指南:我曾经遇到过一个案例,团队用简单的 MD5 对身份证号做脱敏,以为这样就安全了。结果攻击者通过彩虹表反向破解,大量用户信息泄露。后来我们改用加盐的 SHA-256,安全性才达标。记住:脱敏不是走过场,要真正考虑攻击场景。

4.4 数据聚合与计算:从细节到洞察

数据聚合,是 ELT 流程中把原始数据转化为业务价值的关键一步。我习惯把聚合分为两类:

  • 预聚合:在 ELT 阶段就计算好常用的汇总指标,比如每日销售额、用户留存率等。这样查询时直接取用,速度快得多。
  • 实时计算:对于需要最新数据的场景,在查询时动态计算。适合临时分析和探索性分析。
-- 预聚合示例:每日销售汇总
INSERT INTO agg_daily_sales
SELECT 
  order_date,
  product_category,
  COUNT(DISTINCT user_id) AS unique_buyers,
  SUM(order_amount) AS total_revenue,
  AVG(order_amount) AS avg_order_value,
  COUNT(*) / COUNT(DISTINCT user_id) AS purchase_frequency
FROM fact_orders
WHERE order_date = CURRENT_DATE - 1
GROUP BY order_date, product_category;

核心思路:预聚合不是越多越好。我一般遵循「80/20 法则」——只预聚合那些 80% 查询都会用到的指标,剩下的让分析师自己算。这样既保证了查询性能,又保留了灵活性。

计算字段这块,我建议把复杂的业务逻辑封装成 SQL 函数或存储过程。比如计算用户生命周期价值(LTV)、订单转化率这些,写一次,到处用。

-- 创建计算函数示例
CREATE FUNCTION calculate_ltv(
  user_id INT,
  lookback_days INT
) RETURNS DECIMAL(10,2)
AS $$
  SELECT SUM(order_amount) 
  FROM fact_orders
  WHERE user_id = $1
    AND order_date >= CURRENT_DATE - $2
$$ LANGUAGE SQL;

我的经验:做聚合计算时,一定要注意数据倾斜问题。我曾经在处理一个电商数据时,某个爆款商品的订单量是其他商品的几百倍,导致聚合任务跑了好几个小时。后来我加了分桶策略,把大 key 拆成多个小 key 并行处理,性能提升了 10 倍。

4.5 本章知识体系总览

下面这张图,把数据转换的四个核心动作串起来了。你可以把它当作 ELT 转换阶段的检查清单。

数据转换核心:四大动作 数据转换核心 数据清洗 数据标准化 数据脱敏 数据聚合与计算 去重 空值处理 格式统一 编码映射 替换/遮蔽 哈希/泛化 预聚合 实时计算 核心原则 先清洗 → 再标准化 → 脱敏保护 → 最后聚合计算

数据转换这四个动作,顺序很重要。我个人习惯的流程是:先做清洗,把脏数据处理干净;然后做标准化,让数据格式统一;接着做脱敏,保护敏感信息;最后做聚合计算,产出业务指标。这个顺序一旦乱了,后面返工的代价会非常大。

总结一下:数据清洗是基础,标准化是规范,脱敏是底线,聚合是价值。把这四个环节做好了,你的 ELT 流程就成功了一大半。