最不可靠的是用COUNT(*)对比行数,它掩盖NULL、重复、字段错位等问题;应结合聚合函数、分组逻辑与JOIN策略,聚焦业务关键字段校验一致性。

直接用 COUNT(*) 对比行数最不可靠——它掩盖 NULL、重复、字段值错位等所有细节问题。真要快速校验一致性,必须结合聚合函数 + 分组逻辑 + JOIN 策略,聚焦业务关键字段。
用 COUNT(*) = COUNT(字段) 检查字段非空性
迁移后最常漏掉的脏数据是 NULL 混入本该非空的字段(如 order_id、user_id)。只比总行数会完全失效。
-
COUNT(*)统计所有行,含该字段为NULL的记录 -
COUNT(字段)自动跳过NULL,只统计非空值 - 若两者不等,说明存在脏数据;再加
WHERE 字段 IS NULL就能定位具体记录 - 别用
COUNT(字段)替代COUNT(*)做总行数校验——它天然漏数据
用 HAVING 筛分组聚合结果,不是 WHERE
想查“哪些邮箱重复注册”或“哪些订单没关联明细”,必须用 HAVING,因为 WHERE 不能用聚合函数,否则报错 Invalid use of aggregate function。
- 错误写法:
SELECT email FROM users GROUP BY email WHERE COUNT(*) > 1 - 正确写法:
SELECT email, COUNT(*) FROM users GROUP BY email HAVING COUNT(*) > 1 -
HAVING是在分组后过滤,WHERE是在分组前过滤原始行 -
HAVING性能敏感:确保GROUP BY字段有索引,否则扫描开销陡增
用 LEFT JOIN + COUNT(右表字段) 抓孤儿记录
主表有记录但从表没对应明细(比如 orders 存在,但 order_items 中找不到对应 order_id),这是迁移后高频问题。
-
COUNT(oi.id)在LEFT JOIN下,匹配不到时返回 0;而COUNT(*)仍为 1(左表行还在) - 典型语句:
SELECT o.id FROM orders o LEFT JOIN order_items oi ON o.id = oi.order_id GROUP BY o.id HAVING COUNT(oi.id) = 0 - 注意:
oi.order_id IS NULL的记录不会参与 JOIN,需单独用WHERE order_id IS NULL检查“悬空”明细 - 如果右表字段允许 NULL 且业务上不应为空,得额外加
WHERE oi.order_id IS NOT NULL过滤
整数除法导致比例计算失真
做“有效订单占比”这类计算时,COUNT(flag)/COUNT(*) 在 MySQL/PostgreSQL 默认返回整数,0.95 直接变 0,误判一致性失败。
- 错误写法:
SELECT COUNT(status = 'paid') / COUNT(*) FROM orders→ 结果恒为 0 或 1 - 修复方式:显式转浮点,如
ROUND(COUNT(status = 'paid') * 1.0 / COUNT(*), 4) - 更稳妥写法:
CAST(COUNT(status = 'paid') AS DECIMAL) / COUNT(*) - SQLite 更麻烦:它不支持
DECIMAL,需用100.0 * COUNT(...) / COUNT(...)类方式绕过
真正难的不是写出某条 SQL,而是判断哪类不一致该用哪种聚合组合:字段空值、主从缺失、值错位、比例截断——每种背后对应的聚合语义和执行路径都不同,漏掉任一维度,校验就形同虚设。

















