3. 完整性校验:非空校验、默认值填充校验、必填字段检查、关联表完整性检查
完整性,说白了就是数据该有的东西不能少。你想想看,一张订单表里没有订单号,一条客户记录里没有姓名,这数据还能用吗?我做了这么多年数据治理,见过太多因为完整性出问题导致的线上事故。今天我们就来聊聊完整性校验的四个核心场景。
3.1 非空校验:最基础也最容易被忽略
非空校验是完整性检查的第一道防线。它的逻辑很简单:某个字段不能为 NULL,也不能为空字符串。但实际落地时,坑比想象中多。
核心原则:非空校验不仅要检查 NULL,还要检查空字符串、空白字符、以及业务意义上的"空值"(比如日期字段的 '0000-00-00')。
我在项目中遇到过这样一个案例:某系统的用户表里,手机号字段设置了 NOT NULL 约束,但业务方允许用户注册时先不填手机号。结果呢?开发同学用空字符串 '' 来填充,导致后续所有基于手机号的营销活动都出了问题。嗯,这里要注意:数据库约束和业务规则是两回事。
一个靠谱的非空校验规则应该这样写:
-- 基础非空检查
SELECT *
FROM user_info
WHERE mobile IS NULL
OR TRIM(mobile) = ''
OR mobile = '0000-00-00';
-- 批量校验脚本示例
WITH check_result AS (
SELECT
user_id,
CASE
WHEN mobile IS NULL OR TRIM(mobile) = '' THEN '手机号缺失'
WHEN email IS NULL OR TRIM(email) = '' THEN '邮箱缺失'
WHEN real_name IS NULL OR TRIM(real_name) = '' THEN '姓名缺失'
ELSE '通过'
END AS check_status
FROM user_info
)
SELECT check_status, COUNT(*) AS cnt
FROM check_result
GROUP BY check_status;
个人习惯:我一般会在校验规则里加上 TRIM() 函数,因为很多系统在导入数据时会带上不可见字符。曾经有一次,一个字段看起来有值,但实际是 '\t' 制表符,排查了整整半天。
3.2 默认值填充校验:别让默认值变成"毒药"
默认值填充校验,很多人觉得不就是设个 DEFAULT 吗?其实没那么简单。我见过最离谱的情况是:某个系统把性别默认值设为 '未知',结果全表 90% 的记录都是 '未知',这数据基本废了。
默认值填充校验要关注两个层面:
- 默认值是否合理:默认值不能影响业务判断。比如金额字段默认 0 可以,但状态字段默认 '待处理' 就要谨慎。
- 默认值是否被正确填充:检查那些本应有默认值但实际为 NULL 的记录。
-- 检查默认值填充情况
SELECT
table_name,
column_name,
column_default,
COUNT(*) AS null_count
FROM information_schema.columns c
JOIN (
-- 动态拼接查询,这里以具体表为例
SELECT 'order' AS tbl, 'status' AS col FROM dual
) t ON c.table_name = t.tbl AND c.column_name = t.col
WHERE c.column_default IS NOT NULL
AND EXISTS (
SELECT 1 FROM order o WHERE o.status IS NULL
);
避坑指南:我曾经接手过一个项目,默认值填充校验通过了,但数据质量还是差。后来发现,开发同学在代码里写死了默认值,但数据库表结构里没设 DEFAULT。校验规则一定要同时检查数据库约束和代码逻辑。
3.3 必填字段检查:业务规则驱动的完整性
必填字段检查比非空校验更严格。非空校验看的是"有没有值",必填字段检查看的是"该有的值有没有"。举个例子:普通用户可以不填公司名称,但企业用户必须填。这就是业务规则驱动的完整性检查。
我建议把必填字段检查分成两类:
| 检查类型 | 说明 | 示例 |
|---|---|---|
| 全局必填 | 所有记录都必须有值 | 订单号、创建时间、用户ID |
| 条件必填 | 满足特定条件时必须填写 | 企业用户必须填税号、退款单必须填原因 |
-- 条件必填检查示例
SELECT
order_id,
user_type,
company_name,
tax_id,
CASE
WHEN user_type = 'enterprise'
AND (tax_id IS NULL OR TRIM(tax_id) = '')
THEN '企业用户缺少税号'
WHEN user_type = 'enterprise'
AND (company_name IS NULL OR TRIM(company_name) = '')
THEN '企业用户缺少公司名称'
ELSE '通过'
END AS check_result
FROM user_info
WHERE user_type = 'enterprise';
关键点:条件必填字段的检查逻辑一定要和业务方确认清楚。我见过最坑的情况是:业务规则改了,但校验规则没同步更新,导致大量误报和漏报。
3.4 关联表完整性检查:数据孤岛的终结者
关联表完整性检查,说白了就是看表与表之间的关系是否对得上。比如订单表里的用户 ID,在用户表里能不能找到对应的记录?这就是外键约束要做的事。
但现实情况是:很多系统为了性能,不用外键约束。那数据质量怎么保证?只能靠校验规则来补。
常见的关联表完整性检查场景:
- 孤儿记录检查:子表中有记录,但父表中找不到对应的主键。
- 悬空引用检查:父表记录被删除,但子表还在引用。
- 冗余关联检查:同一笔业务在多个关联表中记录不一致。
-- 孤儿记录检查
SELECT
o.order_id,
o.user_id,
'订单表引用了不存在的用户' AS issue_desc
FROM orders o
LEFT JOIN users u ON o.user_id = u.user_id
WHERE u.user_id IS NULL;
-- 悬空引用检查
SELECT
u.user_id,
u.user_name,
'用户已删除但仍有未完成的订单' AS issue_desc
FROM users u
JOIN orders o ON u.user_id = o.user_id
WHERE u.is_deleted = 1
AND o.order_status NOT IN ('completed', 'cancelled');
我的经验:关联表完整性检查最容易出问题的是多对多关系表。比如用户-角色关联表,经常出现角色 ID 指向了不存在的角色。我建议每次做数据迁移或批量删除后,都跑一遍关联完整性检查。
3.5 完整性校验的整体框架
说了这么多,我们来画一张图,把完整性校验的四个维度串起来。这样你写规则的时候,脑子里就有个清晰的框架。
这张图把完整性校验的四个维度拆得很清楚。你写规则的时候,可以对照这个框架,看看自己覆盖了哪些,漏了哪些。
最后提醒一句:完整性校验不是一次性工作。数据在变,业务在变,校验规则也要跟着变。我建议每季度 review 一次完整性校验规则,看看有没有新的业务场景需要覆盖,有没有旧的规则已经失效。