except 是 sql 标准中最直接的差异对比方式,返回左表有而右表无的行,要求两表结构一致,且默认去重;注意 null 被视为相等,mysql 8.0+ 才支持。

用 EXCEPT 找出只在表A里存在的行
SQL标准里最直接的差异对比方式是 EXCEPT(或 MINUS,Oracle用),它返回在左表但不在右表的行。前提是两表结构一致(列数、类型、顺序需兼容)。
- PostgreSQL / SQL Server / SQLite 支持
EXCEPT,MySQL 8.0+ 才支持;MySQL 5.x 得改用LEFT JOIN ... WHERE right_table.pk IS NULL -
EXCEPT默认去重,如果想保留重复行,得用EXCEPT ALL(不是所有数据库都支持) - 注意 NULL 比较:
EXCEPT把两个 NULL 视为相等,而=判断会返回 UNKNOWN,这点容易误判差异 - 示例:
SELECT id, name, value FROM table_a EXCEPT SELECT id, name, value FROM table_b
用 FULL OUTER JOIN 查两边独有的记录
当需要同时看到“仅在A”和“仅在B”的数据时,FULL OUTER JOIN 是最可控的方式,尤其适合调试或生成差异报告。
- MySQL 不支持
FULL OUTER JOIN,得用LEFT JOIN+RIGHT JOIN+UNION模拟 - 连接条件必须覆盖所有用于判断“相同”的字段,漏掉一个就可能把本该匹配的行判成差异
- 记得用
IS NULL判断缺失侧,别用= NULL——那永远不成立 - 示例(PostgreSQL):
SELECT 'only_in_a' AS src, a.* FROM table_a a FULL OUTER JOIN table_b b ON a.id = b.id AND a.name = b.name WHERE b.id IS NULL OR a.id IS NULL
用 CHECKSUM 或 HASH 做整行快速比对(大数据量场景)
当表有几十万行、且字段多、类型杂时,逐字段比较性能差,可先算行级哈希再比对。
- PostgreSQL 用
md5(row.*)::text,SQL Server 用BINARY_CHECKSUM(*),MySQL 可用SHA2(CONCAT_WS('|', col1, col2, ...), 256) - 字符串拼接前要处理 NULL:
COALESCE(col, ''),否则整个CONCAT返回 NULL - 哈希碰撞概率极低但存在,生产环境关键比对不能只依赖哈希,建议先用哈希筛出候选差异,再对小结果集做字段级确认
- 注意时区、字符集、尾部空格等隐式差异会影响哈希值,比对前确保两端数据已标准化
WHERE 子句里写字段级差异判断(精准定位哪列不同)
如果目标不是“哪些行不同”,而是“哪些字段值不一致”,就得展开写每个字段的比较逻辑。
- 用
IS DISTINCT FROM(PostgreSQL)或(a.col != b.col) OR (a.col IS NULL) != (b.col IS NULL)来安全比较含 NULL 的字段 - 字符串比较注意大小写和空格:
TRIM(UPPER(a.name)) != TRIM(UPPER(b.name)) - 数值精度问题:浮点列慎用
!=,优先转为ROUND(col, 6)再比 - 别忘了主键/唯一键字段也要参与判断——有些差异其实是因关联键不一致导致的“假差异”
实际执行时,先确认数据库版本是否支持所需语法,再根据数据量选策略:小表直接 EXCEPT,中等表用 FULL OUTER JOIN,大表分步哈希+字段验证。字段级差异逻辑最容易写错,尤其是 NULL 和类型隐式转换,每次改完最好拿几条手工验证一下。











