4、完整性校验规则:非空校验、默认值校验、记录数波动检测、字段覆盖率统计
完整性,说白了就是数据「该有的都有,不该缺的别缺」。
我见过太多项目,上线第一天跑报表,发现关键字段全是空值,业务方直接炸锅。
所以这一章,咱们把完整性校验的四个核心手段讲透。
4.1 非空校验:最基础也最容易被忽视
非空校验,就是检查某个字段不能为 NULL 或空字符串。
听起来简单吧?但坑不少。
核心原则: 不是所有字段都需要非空校验。只有业务上「必须存在」的字段才加。
我个人习惯,先跟业务方确认哪些字段是「必填项」。
比如用户注册场景:手机号、身份证号、注册时间,这些必须非空。
但「用户昵称」这种,很多系统允许为空,就别瞎校验。
校验规则示例:
-- 非空校验 SQL 示例
SELECT
table_name,
column_name,
COUNT(*) AS total_rows,
SUM(CASE WHEN column_value IS NULL OR column_value = '' THEN 1 ELSE 0 END) AS null_count,
ROUND(SUM(CASE WHEN column_value IS NULL OR column_value = '' THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 2) AS null_rate
FROM data_quality_check
GROUP BY table_name, column_name
HAVING null_rate > 0;
小技巧: 我建议把非空校验做成「阈值可配置」的。有些字段允许 1% 以内的空值,比如历史数据迁移时,早期数据可能不规范。直接设成 0% 容忍,反而会误报。
4.2 默认值校验:别让默认值变成「脏数据」
默认值,是数据库设计时给字段设的「保底值」。
但我在项目中遇到过,默认值本身就有问题。
举个例子:
一张订单表,status 字段默认值是 0,代表「待支付」。
结果上线半年后,发现大量订单状态是 0,但实际已经支付完成了。
为什么?因为程序里忘记更新状态字段,默认值一直没被覆盖。
默认值校验要检查两件事:
- 默认值是否合理 —— 跟业务方确认,这个默认值在真实场景中是否真的「默认」。
- 默认值占比是否异常 —— 如果某个字段 90% 都是默认值,大概率是程序没正确赋值。
-- 默认值占比检测 SQL
SELECT
column_name,
default_value,
COUNT(*) AS default_count,
COUNT(*) * 100.0 / (SELECT COUNT(*) FROM target_table) AS default_rate
FROM target_table
WHERE column_value = default_value
GROUP BY column_name, default_value
HAVING default_rate > 50; -- 超过 50% 就报警
注意: 时间字段的默认值要特别小心。比如
create_time 默认值是 CURRENT_TIMESTAMP,但如果批量导入数据时没传这个字段,所有记录的时间戳都一样,这算不算异常?我个人认为,如果批量导入超过 100 条记录且时间戳完全相同,就应该触发告警。
4.3 记录数波动检测:发现数据「少了」或「多了」
记录数波动,是完整性校验里最直观的指标。
数据突然少了一半,或者翻了三倍,肯定有问题。
我记得有一次,凌晨跑批任务出了问题,导致某张表只导入了 30% 的数据。
第二天早上业务方发现报表数据对不上,排查了半天。
如果当时有记录数波动检测,凌晨就能收到告警,根本不用等到早上。
检测逻辑:
- 设定一个「基准值」:通常是最近 7 天或 30 天的日均记录数。
- 设定「波动阈值」:比如 ±20%。
- 每天对比实际记录数与基准值,超出阈值就告警。
-- 记录数波动检测示例(伪代码)
WITH daily_count AS (
SELECT
DATE(create_time) AS dt,
COUNT(*) AS record_count
FROM target_table
WHERE create_time >= DATE_SUB(CURRENT_DATE, INTERVAL 30 DAY)
GROUP BY DATE(create_time)
),
baseline AS (
SELECT
AVG(record_count) AS avg_count,
STDDEV(record_count) AS std_count
FROM daily_count
WHERE dt >= DATE_SUB(CURRENT_DATE, INTERVAL 7 DAY)
)
SELECT
CURRENT_DATE AS check_date,
(SELECT record_count FROM daily_count WHERE dt = CURRENT_DATE) AS today_count,
baseline.avg_count,
ROUND(ABS(today_count - avg_count) * 100.0 / avg_count, 2) AS fluctuation_rate
FROM baseline
WHERE ABS(today_count - avg_count) * 100.0 / avg_count > 20; -- 波动超过 20% 告警
避坑指南: 我曾经把阈值设成 ±10%,结果每周都误报。后来发现,周末的数据量本来就比工作日少 30%。所以,我建议按「星期几」分别计算基准值。周一的基准值用过去四周的周一数据算,周二用周二的数据算,这样更准。
4.4 字段覆盖率统计:知道哪些字段「没人用」
字段覆盖率,统计的是某个字段「有值」的记录占比。
如果一个字段的覆盖率长期低于某个阈值,说明这个字段可能设计冗余,或者业务根本没用上。
我见过一个系统,表里有 50 多个字段,但实际业务只用到了 20 个。
剩下的 30 个字段,要么全是 NULL,要么全是默认值。
这不仅浪费存储,还让数据维护变得复杂。
覆盖率统计规则:
| 覆盖率范围 | 建议操作 |
|---|---|
| 100% | 正常,无需处理 |
| 80% - 99% | 检查缺失原因,可能是部分业务场景不需要 |
| 50% - 79% | 建议跟业务方确认,是否真的需要这个字段 |
| < 50% | 强烈建议评估是否可废弃或迁移 |
-- 字段覆盖率统计 SQL
SELECT
column_name,
COUNT(*) AS total_rows,
SUM(CASE WHEN column_value IS NOT NULL AND column_value != '' THEN 1 ELSE 0 END) AS filled_count,
ROUND(SUM(CASE WHEN column_value IS NOT NULL AND column_value != '' THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 2) AS coverage_rate
FROM target_table
GROUP BY column_name
ORDER BY coverage_rate ASC;
注意: 覆盖率低不一定是坏事。比如「用户注销时间」这个字段,只有注销的用户才有值,覆盖率可能只有 5%。这种业务上合理的低覆盖率,不需要告警。所以,覆盖率阈值也要按字段单独配置。
4.5 完整性校验规则体系总览
这四个手段,其实覆盖了完整性校验的四个维度:
- 非空校验 —— 检查「必须有的」有没有
- 默认值校验 —— 检查「默认的」是不是对的
- 记录数波动 —— 检查「总量」有没有异常
- 字段覆盖率 —— 检查「每个字段」是不是被合理使用
把它们组合起来,就是一个完整的完整性校验体系。