SQL 数据清洗与分析案例

3次阅读

where中= NULL失效,因null是缺失标记,等值比较结果为unknown;须用is null;group by非聚合字段需显式分组或聚合;清洗手机号应优先用regexp_replace剔除非数字字符。

SQL 数据清洗与分析案例

WHERE 条件里用 NULL 判断会失效?用 IS NULL 而不是 = NULL

sqlNULL 不是值,而是“缺失”的标记,所以任何和 NULL 的等值比较(比如 = NULL!= NULL)结果都是 UNKNOWN,被 WHERE 当作假——查不到数据,但也不报错,容易误以为数据不存在。

  • WHERE col = NULL → 永远不匹配,哪怕该列全是 NULL
  • 正确写法是 WHERE col IS NULLWHERE col IS NOT NULL
  • 如果要统一处理空值再比较,可配合 COALESCE(col, '')NULLIF(col, ''),但要注意类型隐式转换风险(比如把数字转成字符串后做 =

GROUP BY 后字段必须在 select 中出现或被聚合?不是所有数据库都强制

mysql 默认允许 SELECT a, b FROM t GROUP BY ab 未聚合也未分组),但 postgresql、SQL Server、标准 SQL 都会报错:column "b" must appear in the GROUP BY clause or be used in an aggregate function。这常导致迁移或跨库查询时突然失败。

  • 安全写法:所有非聚合字段都放进 GROUP BY,或明确用聚合函数包裹,如 MAX(b)STRING_AGG(b, ',')
  • MySQL 8.0+ 开启 ONLY_FULL_GROUP_BY 模式后行为与其他数据库一致,建议开发环境默认启用
  • 别依赖 SELECT * + GROUP BY——字段增减会直接让语句不可控

清洗手机号、身份证号这类文本字段,TRIM()REPLACE() 不够,得用 REGEXP_REPLACE()

真实数据里常见空格、换行、中文括号、短横线混在号码中,比如 "138- 0013 8000n""(138)00138000"TRIM() 只能去首尾,REPLACE() 写一嵌套可读性差且漏情况。

  • PostgreSQL / MySQL 8.0+ 支持 REGEXP_REPLACE(col, '[^0-9]', '', 'g'),一次性剔除非数字字符
  • 注意:MySQL 5.7 不支持 g 标志,需用循环或应用层处理;sqlite 默认无正则,得加载扩展或改用 replace() 多层嵌套(不推荐)
  • 清洗后建议加长度校验,比如 Length(cleaned_phone) = 11,避免“13800138000123”这种超长脏数据混入

分析时用 LEFT JOIN 却没查到右表数据?检查 ON 条件里的空值和类型隐式转换

LEFT JOIN 本应保留左表全部记录,但若右表关联字段含 NULL 或类型不一致(比如左表是 VARCHAR 的 ID,右表是 int),ON 条件永远不成立,右表字段全为 NULL,看起来像“没连上”。

  • 先确认右表关联字段是否有 NULLSELECT count(*) FROM right_table WHERE join_col IS NULL
  • 检查字段类型是否一致:SELECT pg_typeof(join_col) FROM right_table LIMIT 1(PostgreSQL)或 DESCRIBE right_table(MySQL)
  • 避免隐式转换:显式转类型,如 ON CAST(l.id AS TEXT) = r.str_id,比依赖数据库自动转换更可靠

空值和类型问题在清洗阶段不暴露,一到关联分析就断链,而且错误安静——没报错,只少数据。

text=ZqhQzanResources