数据校验核心概念:五类校验方法详解

数据校验这件事,说白了就是给数据做体检。我在数仓项目里摸爬滚打这些年,见过太多因为校验不到位而翻车的案例。今天咱们就把五类最核心的校验方法掰开揉碎了讲清楚。

核心观点:数据校验不是事后补救,而是贯穿ETL全流程的质量防线。五类校验方法各有侧重,组合使用才能覆盖绝大多数数据质量问题。

数据校验五类核心方法体系图 数据校验核心方法 记录数校验 空值校验 重复值校验 业务规则校验 跨表关联校验 各方法核心关注点 • 记录数校验:源表与目标表行数是否一致 • 空值校验:关键字段是否存在不应有的NULL • 重复值校验:主键或业务唯一键是否重复 • 业务规则校验:数据是否符合业务逻辑约束 • 跨表关联校验:表间关联关系是否完整一致 建议:按此顺序逐层校验,先数量后质量,先单表后跨表

1. 记录数校验:最基础也最容易被忽视

记录数校验,就是对比源表和目标表的行数。听起来简单吧?但我见过太多团队在这上面翻车。

我记得有一次做电商数仓迁移,源表有1.2亿条订单数据,ETL跑完后目标表只有1.1亿。差了1000万条!排查了半天,发现是某个分区条件写错了,把上个月的数据全过滤掉了。要是没有记录数校验,这问题可能要等到业务方发现数据对不上才暴露。

我的习惯做法:在ETL的每个阶段都做记录数校验——抽取后、转换后、加载后。三个数字对得上,才能放心往下走。

实现方式也很直接:

-- 源表记录数
SELECT COUNT(*) AS src_cnt FROM source_orders WHERE dt = '2024-01-15';

-- 目标表记录数
SELECT COUNT(*) AS tgt_cnt FROM dwd_orders WHERE dt = '2024-01-15';

-- 对比结果
SELECT 
  src_cnt,
  tgt_cnt,
  CASE WHEN src_cnt = tgt_cnt THEN 'PASS' ELSE 'FAIL' END AS status
FROM (SELECT COUNT(*) AS src_cnt FROM source_orders WHERE dt = '2024-01-15') a
CROSS JOIN (SELECT COUNT(*) AS tgt_cnt FROM dwd_orders WHERE dt = '2024-01-15') b;

注意:记录数一致不代表数据正确。我曾经遇到过源表有重复数据,目标表去重后记录数反而少了——这时候记录数校验会报错,但其实是正确的业务逻辑。所以,记录数校验要结合其他校验方法一起用。

2. 空值校验:别让NULL悄悄溜进数仓

空值校验,检查的是关键字段里有没有不该出现的NULL。你想想看,一个订单表的订单金额字段如果是NULL,那下游的汇总报表会出什么问题?

我个人习惯把字段分成三类:

字段类型 空值容忍度 校验规则
主键/业务键 零容忍 NULL率 = 0%
核心业务字段 低容忍 NULL率 < 1%
辅助/扩展字段 可容忍 NULL率 < 20%

实际项目中,我一般这样写空值校验:

-- 检查核心字段空值率
SELECT 
  'order_id' AS field_name,
  SUM(CASE WHEN order_id IS NULL THEN 1 ELSE 0 END) AS null_count,
  ROUND(SUM(CASE WHEN order_id IS NULL THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 2) AS null_rate
FROM dwd_orders
WHERE dt = '2024-01-15'

UNION ALL

SELECT 
  'order_amount' AS field_name,
  SUM(CASE WHEN order_amount IS NULL THEN 1 ELSE 0 END) AS null_count,
  ROUND(SUM(CASE WHEN order_amount IS NULL THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 2) AS null_rate
FROM dwd_orders
WHERE dt = '2024-01-15';

避坑指南:我曾经遇到过一个案例,某个字段的空值率突然从0%跳到了15%。排查后发现是上游系统改了接口,新版本把某个字段改成了可选。要不是空值校验及时发现,这个脏数据就会一路污染到报表层。

3. 重复值校验:主键的唯一性不能妥协

重复值校验,说白了就是检查主键或业务唯一键有没有重复。这是数据质量的底线,没有商量余地。

为什么这么说?你想想看,如果订单表里同一个订单ID出现两次,那汇总订单金额的时候就会翻倍。业务方拿着翻倍的报表去对账,那画面太美我不敢看。

-- 检查主键重复
SELECT 
  order_id,
  COUNT(*) AS dup_count
FROM dwd_orders
WHERE dt = '2024-01-15'
GROUP BY order_id
HAVING COUNT(*) > 1;

-- 如果返回0行,说明没有重复
-- 如果返回有数据,就需要排查重复原因

嗯,这里要注意:重复值校验不只是针对单字段主键。复合主键也要检查:

-- 复合主键重复检查
SELECT 
  order_id,
  product_id,
  COUNT(*) AS dup_count
FROM dwd_order_details
WHERE dt = '2024-01-15'
GROUP BY order_id, product_id
HAVING COUNT(*) > 1;

我的经验:重复值校验最好在ETL的每个增量批次都跑一次。全量跑一次只能发现问题,增量跑才能及时拦截问题。我曾经帮一个团队排查了三天数据对不上的问题,最后发现是某个增量批次重复加载了同一批数据。

4. 业务规则校验:让数据符合真实世界的逻辑

业务规则校验,是五类校验里最灵活也最考验业务理解能力的。它检查的是数据是否符合业务逻辑约束。

举个例子:

  • 订单金额必须大于0
  • 下单时间必须早于支付时间
  • 会员等级和折扣率必须匹配
  • 年龄字段不能超过150岁

这些规则看起来简单,但实际落地时坑不少。我记得有一次做金融数仓,业务规则要求"交易金额不能超过账户余额"。听起来合理吧?但实际跑起来发现,有些交易是跨行转账,到账有延迟,余额还没更新。这时候就需要和业务方确认:到底是以交易发起时的余额为准,还是以实际扣款时的余额为准?

-- 业务规则校验示例:订单金额必须为正数
SELECT 
  order_id,
  order_amount,
  '订单金额不能为负数或零' AS rule_name,
  'FAIL' AS check_result
FROM dwd_orders
WHERE dt = '2024-01-15'
  AND order_amount <= 0;

-- 时间逻辑校验:支付时间不能早于下单时间
SELECT 
  order_id,
  order_time,
  pay_time,
  '支付时间不能早于下单时间' AS rule_name,
  'FAIL' AS check_result
FROM dwd_orders
WHERE dt = '2024-01-15'
  AND pay_time IS NOT NULL
  AND pay_time < order_time;

重要提醒:业务规则校验一定要和业务方确认规则。我见过技术团队自己拍脑袋定规则,结果把正常数据全拦截了。业务规则校验的阈值和逻辑,必须经过业务方签字确认。

5. 跨表关联校验:表与表之间的数据要能对得上

跨表关联校验,检查的是不同表之间的关联关系是否完整一致。数仓里表多了,表与表之间的数据一致性就成了大问题。

最常见的场景:

  • 事实表中的外键,在维度表中是否存在对应的记录
  • 两张表通过某个字段关联后,关联上的记录数是否合理
  • 汇总表的数据,和明细表汇总后的数据是否一致

我习惯用这种方式做关联校验:

-- 检查事实表的外键是否都在维度表中存在
SELECT 
  COUNT(*) AS total_fact_records,
  SUM(CASE WHEN dim.product_id IS NULL THEN 1 ELSE 0 END) AS orphan_records,
  ROUND(SUM(CASE WHEN dim.product_id IS NULL THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 2) AS orphan_rate
FROM dwd_order_details fact
LEFT JOIN dim_product dim ON fact.product_id = dim.product_id
WHERE fact.dt = '2024-01-15';

-- 明细汇总 vs 汇总表对比
SELECT 
  '明细汇总' AS data_source,
  SUM(order_amount) AS total_amount
FROM dwd_orders
WHERE dt = '2024-01-15'

UNION ALL

SELECT 
  '汇总表' AS data_source,
  total_amount
FROM dws_daily_order_summary
WHERE dt = '2024-01-15';

实战经验:跨表关联校验最容易发现"孤儿记录"问题。我曾经在一个客户数仓项目里,发现事实表里有30%的客户ID在客户维度表里找不到。排查后发现是ETL加载维度表时漏了一个数据源。要不是跨表关联校验,这个问题可能要等到业务方做客户分析时才发现。

五类校验方法的对比与选择

最后,我整理了一个对比表,方便你在实际项目中快速选择:

校验方法 检查对象 发现的问题类型 执行频率 优先级
记录数校验 表级 数据丢失、重复加载 每次ETL P0
空值校验 字段级 字段缺失、上游变更 每次ETL P0
重复值校验 记录级 主键冲突、重复数据 每次ETL P0
业务规则校验 逻辑级 业务逻辑异常 每日/每周 P1
跨表关联校验 表间级 关联断裂、数据不一致 每日 P1

这五类校验方法,说白了就是一套组合拳。单用哪一种都有盲区,组合起来才能构建完整的数据质量防线。我个人建议:先把P0的三类校验做成自动化脚本,每天定时跑;P1的两类校验可以做成周报或月报的形式,定期检查。

最后说一句:数据校验不是一次性工作,而是需要持续迭代的。随着业务变化,校验规则也要跟着调整。保持校验脚本的灵活性和可维护性,比一次性写出完美的校验逻辑更重要。