第三章:完整性指标——记录缺失率、字段空值率、主键唯一性比例

完整性是数据质量的底线。数据如果缺胳膊少腿,后面的分析全是白搭。我第一次带数据质量项目时,业务方指着报表说「这数字不对」,查了半天,原来是源表丢了 30% 的记录,但大伙儿只看字段没看行数。

这一章咱们就拆开三个最常用的完整性指标:记录缺失率、字段空值率、主键唯一性比例。说句实话,能把这三个用对,日常 80% 的完整性监控就不慌了。

3.1 记录缺失率 —— 你的数据到底少了几行?

定义:期望行数和实际行数之间的差异比例。

举个例子,CRM 系统每天应该同步 10 万条订单,但今天只到了 8 万,记录缺失率就是 20%。

计算公式:

记录缺失率 = (期望记录数 - 实际记录数) / 期望记录数 × 100%

实际计算脚本(Python + SQL 思路):

# 简单写法:对比两天的行数
expected_count = 100000   # 从业务系统元数据拿
actual_count = spark.sql("SELECT COUNT(*) FROM orders WHERE dt = '2025-03-20'").collect()[0][0]
missing_rate = (expected_count - actual_count) / expected_count
print(f"记录缺失率:{missing_rate:.2%}")

这里有个坑。期望记录数从哪来?千万别拍脑袋。我曾经接过一个项目,业务方说「每天应该有 50 万条」,后来发现他们的期望值是基于三年前的系统压测数据算的。我建议,要么从源系统的调度日志拿,要么用历史均值加 3-sigma 做动态基线。

避坑指南:我曾经因为没区分「业务正常波动」和「真实缺失」吃过亏。比如大促期间订单量翻倍,如果还用平常的基线算缺失率,指标直接飙红。要加「业务日历」或「同比/环比」的上下文判断。

3.2 字段空值率 —— 哪个字段在跟你玩失踪?

记录没丢,但关键字段是空的,这也很要命。字段空值率衡量的是单个字段为空的比例。

计算公式:

字段空值率 = 该字段为空的记录数 / 总记录数 × 100%

计算案例 —— 检查「手机号」字段:

SELECT 
    COUNT(*) AS total_rows,
    SUM(CASE WHEN phone IS NULL OR phone = '' THEN 1 ELSE 0 END) AS null_phone,
    ROUND(
        SUM(CASE WHEN phone IS NULL OR phone = '' THEN 1 ELSE 0 END) * 100.0 / COUNT(*),
        2
    ) AS null_rate_pct
FROM user_profile
WHERE dt = '2025-03-20';
-- 输出示例
total_rows  |  null_phone  |  null_rate_pct
200000      |  8500        |  4.25

4.25% 算不算高?这要看场景。用户注册场景,手机号空值是 0 才行;但匿名浏览日志,手机号为空可能是常态。我习惯为每个字段画一条「容忍线」:核心业务字段 0%,可选字段 < 5%,备注型字段 < 20%。

嗯,这里还要注意「空」的边界。NULL 是空,空字符串 '' 是不是空?空格字符串呢?不同的 ETL 工具处理方式不一样。我建议在数据接入层统一做空值标准化,否则空值率算出来也不准。

个人习惯:我在做字段空值监控时,会同时看「空值率」和「空值分布的时间趋势」。比如空值率突然从 1% 跳到 10%,很可能是上游字段映射改了,或者是新版本的 bug。

3.3 主键唯一性比例 —— 你的数据表有「身份证」吗?

主键重复,是数据质量的大忌。想象一下,订单表里有两条 order_id='123',你说聚合查询时算一笔还是两笔?

计算公式:

主键唯一性比例 = 不重复的主键数 / 总记录数 × 100%

理想的表,这个比值应该是 100%。但凡小于 100%,就有重复记录了。

脚本检查:

-- 先看有没有重复主键
SELECT 
    COUNT(*) AS total_count,
    COUNT(DISTINCT order_id) AS unique_count,
    ROUND(
        COUNT(DISTINCT order_id) * 100.0 / COUNT(*),
        4
    ) AS uniqueness_pct
FROM transaction_log
WHERE dt = '2025-03-20';

找出具体重复的条数:

-- 找出所有重复的 order_id
SELECT order_id, COUNT(*) AS dup_count
FROM transaction_log
WHERE dt = '2025-03-20'
GROUP BY order_id
HAVING COUNT(*) > 1;
-- 输出示例
order_id    |  dup_count
TXN-202503-001 |  2
TXN-202503-045 |  3

主键重复的原因我见过几种:
1. 上游重复投递了消息(Kafka 重试就常见)
2. 跑批任务被重复调度
3. 源系统没有做幂等写入

解决办法也简单:入库前加 INSERT OVERWRITE 或者在 ETL 层做 DISTINCT + ROW_NUMBER 去重。

关键认知:主键唯一性比例必须严格为 100%,这是硬指标。我建议在数据接入层做「断言」,一旦发现不等于 100%,立刻触发告警并阻断下游。等数据修好再放行,别把脏数据往下流。

3.4 指标选择:到底用哪个?

这三个指标不是互相替代的关系,而是互补的检查层。我一般这样组合:

场景 推荐指标组合 说明
流水/日志型表(如埋点日志) 记录缺失率 + 字段空值率 重点保证记录不丢,关键维度(如时间戳)不空
实体/主数据表(如用户表) 主键唯一性比例 + 字段空值率 必须确保主键不重复,核心属性完整
中间表/汇总表 记录缺失率 + 主键唯一性比例 保证聚合过程没丢数据,输出主键无重复
接口/API 输出表 三个全上 少记录、空字段、重复主键都会导致接口异常

我想表达的是,不要贪多。团队初期能把这三个指标跑稳,数据质量就已经超过大多数公司了。

3.5 综合框架:一张图理清三大指标

下面这张 SVG 图帮你快速抓住这三个指标的检查范围和优先级。

完整性三大指标检查框架 原始数据表 记录缺失率 检查:行数是否完整 字段空值率 检查:字段是否为空 主键唯一性比例 检查:主键是否重复 业务影响: 记录缺 → 报表对不上账 | 字段空 → 风控模型不准 | 主键重 → 数据聚合翻倍 推荐检查频率 流水日志:每批次 核心实体:每 6h 汇总表:每日 接口表:实时 建议: • 先跑主键唯一性(最快暴露问题) → 再跑记录缺失率 → 最后看字段空值率 • 空值率设置「环比涨幅告警」,避免反复调整阈值

这张图给了一个执行顺序:先查主键唯一性,再查记录缺失率,最后查字段空值率。为什么这么排?主键重复最容易导致下游数据膨胀,也最好修;记录缺失影响全局;字段空值虽然烦人,但通常可容忍一段时间。

我的经验:刚开始做完整性监控,别想着一次把三个指标的精细阈值都设对。先设一个「宽松版」(比如主键唯一性 < 100% 才告警,空值率 > 50% 才告警),跑两周收集基线,再慢慢收紧。否则告警淹没,团队很快就麻木了。

好了,完整性这块,把行数、空值、主键盯住了,你就抓住了数据质量的命门。动手试试,先选一张你手头的表跑一遍这三个指标,看看有没有意外收获。