1. 项目背景与需求解析
在数据仓库项目中,数据质量治理是确保分析结果准确性的基石。最近在负责某金融行业数据治理项目时,我们基于GaussDB 200构建的企业级数仓遇到了一个典型问题:业务系统产生的数据中存在大量NULL值与空字符串混用的情况,导致下游报表统计出现偏差。
具体表现为:
- 业务系统将未填写字段存储为NULL
- ETL过程部分字段被转换为空字符串('')
- 报表工具对NULL和''的处理逻辑不一致
- 历史数据中存在空白字符(' ')等特殊情况
2. 技术方案设计思路
2.1 GaussDB 200的空值特性
GaussDB 200作为华为云企业级分布式数据仓库,在处理NULL值时有其特殊机制:
- 列存表仅支持NULL/NOT NULL约束
- 与Oracle不同,不支持DEFAULT NULL语法
- COALESCE函数处理逻辑与PostgreSQL兼容
- 空字符串与NULL在比较运算中被视为不同值
2.2 批量检测方案选型
经过技术评估,我们确定了三种实现路径:
方案对比表:
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 系统表查询 | 执行快 | 精度低 | 初步筛查 |
| 动态SQL扫描 | 结果准 | 耗时长 | 精确检查 |
| 存储过程 | 可复用 | 开发慢 | 定期任务 |
最终选择动态SQL方案,因其能:
- 准确识别所有空值形态
- 生成可追溯的检查报告
- 适配不同表结构
3. 核心脚本实现详解
3.1 系统表元数据提取
-- 获取指定schema下所有表字段信息 WITH table_columns AS ( SELECT n.nspname AS schema_name, c.relname AS table_name, a.attname AS column_name, pg_catalog.format_type(a.atttypid, a.atttypmod) AS data_type FROM pg_catalog.pg_attribute a JOIN pg_catalog.pg_class c ON a.attrelid = c.oid JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid WHERE a.attnum > 0 AND NOT a.attisdropped AND n.nspname = 'target_schema' )3.2 动态生成检查语句
-- 构建动态检查SQL SELECT 'SELECT ''' || schema_name || '.' || table_name || '.' || column_name || ''' AS object_path, COUNT(*) AS null_count, (SELECT COUNT(*) FROM ' || schema_name || '.' || table_name || ') AS total_rows, ROUND(COUNT(*) * 100.0 / (SELECT COUNT(*) FROM ' || schema_name || '.' || table_name || '), 2) AS null_percentage FROM ' || schema_name || '.' || table_name || ' WHERE ' || column_name || ' IS NULL OR ' || column_name || ' = ''''' FROM table_columns;3.3 完整批处理脚本
DO $$ DECLARE query_text TEXT; result_record RECORD; report_cursor REFCURSOR; BEGIN -- 创建临时表存储结果 CREATE TEMP TABLE null_check_results ( object_path VARCHAR(512), null_count BIGINT, total_rows BIGINT, null_percentage NUMERIC(5,2), check_time TIMESTAMP ); -- 遍历所有表字段 FOR query_text IN SELECT 'INSERT INTO null_check_results SELECT ''' || n.nspname || '.' || c.relname || '.' || a.attname || ''', COUNT(*) FILTER (WHERE ' || a.attname || ' IS NULL OR ' || a.attname || ' = ''''''), COUNT(*), ROUND(COUNT(*) FILTER (WHERE ' || a.attname || ' IS NULL OR ' || a.attname || ' = '''''') * 100.0 / COUNT(*), 2), NOW() FROM ' || n.nspname || '.' || c.relname FROM pg_catalog.pg_attribute a JOIN pg_catalog.pg_class c ON a.attrelid = c.oid JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid WHERE a.attnum > 0 AND NOT a.attisdropped AND n.nspname = 'target_schema' LOOP EXECUTE query_text; END LOOP; -- 生成分析报告 OPEN report_cursor FOR SELECT * FROM null_check_results WHERE null_count > 0 ORDER BY null_percentage DESC; -- 此处可添加邮件发送或日志记录逻辑 END $$;4. 性能优化实践
4.1 分批处理策略
对于超大型表(>1亿行),采用分片检查:
-- 添加分片检查条件 WHERE (ctid::text::point)[0] % 10 = 0 -- 检查10%样本 AND ($column_name IS NULL OR $column_name = '')4.2 并行执行控制
通过dbe_perf.session视图监控:
SELECT * FROM dbe_perf.session WHERE query LIKE '%null_check_results%';4.3 结果缓存机制
利用物化视图缓存历史结果:
CREATE MATERIALIZED VIEW null_check_history AS SELECT *, NOW() AS check_time FROM null_check_results;5. 典型问题排查指南
5.1 权限问题
报错:permission denied for relation xxx
解决方案:
GRANT SELECT ON ALL TABLES IN SCHEMA target_schema TO check_user;5.2 长事务阻塞
报错:canceling statement due to conflict with recovery
处理方法:
SET lock_timeout = '5s'; SET statement_timeout = '10min';5.3 特殊字符处理
对于包含特殊字符的字段名:
WHERE ("column-name" IS NULL OR "column-name" = '')6. 数据治理建议
根据检查结果实施分级治理:
关键字段NULL率>5%:
- 联系业务系统整改
- ETL过程添加默认值
- 建立数据质量监控规则
非关键字段NULL率>30%:
- 评估字段必要性
- 考虑合并或废弃字段
所有异常空字符串:
- 统一转换为NULL
- 添加清洗转换规则
实际项目中,该方案帮助我们发现了12个关键业务表中23个字段的空值异常,经过治理后报表差异率从7.8%降至0.3%。建议每月定期执行检查,将结果纳入数据质量KPI考核体系。