能,但必须搭配条件筛选和分组逻辑——聚合函数用于定位重复、异常值及脏数据分布,需结合having、case when和cte才能实现可执行清洗。

能,但必须搭配条件筛选和分组逻辑一起用——聚合函数本身不清洗数据,只帮你定位问题。
用 COUNT + GROUP BY 快速揪出重复记录
重复数据是脏数据里最常见也最容易被忽略的类型。单纯 SELECT * 看不出问题,但一加 GROUP BY 就暴露了。
- 查业务主键重复(比如
user_id和email联合唯一):SELECT user_id, email, COUNT(*) FROM users GROUP BY user_id, email HAVING COUNT(*) > 1 - 查完全重复行(所有字段都一样):
SELECT *, COUNT(*) FROM orders GROUP BY order_id, customer_id, amount, created_at HAVING COUNT(*) > 1 - 注意:MySQL 8.0+ 支持直接
GROUP BY *,但多数旧版本必须显式列出所有列,漏一个就可能误判
用 MIN/MAX/AVG 检测数值型异常值
聚合函数配合 HAVING 是识别越界、负值、离群点的最快路径,比逐行扫描高效得多。
- 查价格异常:
SELECT product_id, MIN(price), MAX(price), AVG(price) FROM products GROUP BY product_id HAVING MIN(price) 1000000 - 查时间倒置(注册时间晚于订单时间):
SELECT user_id, MIN(regist_time), MAX(order_time) FROM users u JOIN orders o ON u.id = o.user_id GROUP BY user_id HAVING MIN(regist_time) > MAX(order_time) - 陷阱:
AVG对 NULL 不敏感,但若字段大量为 NULL,平均值会失真;建议同步查COUNT(*)和COUNT(column_name)看空值占比
用 SUM + CASE WHEN 统计脏数据分布比例
光知道“有脏数据”不够,得知道“脏在哪、有多脏”,否则清洗优先级没法排。
- 统计手机号格式问题占比:
SELECT SUM(CASE WHEN phone REGEXP '^[0-9]{11}$' THEN 0 ELSE 1 END) * 100.0 / COUNT(*) AS bad_phone_pct FROM users - 统计状态字段非法值数量:
SELECT status, COUNT(*) FROM orders WHERE status NOT IN ('paid', 'cancelled', 'pending') GROUP BY status - 关键点:别只用
COUNT(*),要结合CASE WHEN构造布尔指标,才能算出百分比、覆盖率等可决策指标
聚合结果不能直接 UPDATE,必须转成子查询或 CTE
这是新手最容易卡住的地方:聚合输出的是汇总行,而 UPDATE 需要操作原始行。中间必须搭一座桥。
- 错误写法(语法报错):
UPDATE users SET phone = REPLACE(phone, '-', '') WHERE phone IN (SELECT phone FROM users GROUP BY phone HAVING COUNT(*) > 1) - 正确做法(用 CTE 标记问题行):
WITH dirty_phones AS (SELECT phone FROM users WHERE phone REGEXP '[^0-9]' GROUP BY phone)UPDATE users SET phone = REGEXP_REPLACE(phone, '[^0-9]', '') WHERE phone IN (SELECT phone FROM dirty_phones) - 更安全的做法:先用
SELECT验证 CTE 结果,再套UPDATE;生产环境务必加WHERE限定范围,避免全表误更新
真正难的不是写出聚合语句,而是把“哪里有问题”和“怎么修”连成一条可执行、可验证、可回滚的链路。每一步聚合结果都要能映射回原始记录,否则清洗就只是纸上谈兵。











