MySQL不支持EXCEPT,需用LEFT JOIN+IS NULL模拟差集:将左表设为待查差异数据,右表按全字段关联并筛选右表匹配为空的记录,注意NULL安全比较、索引优化及字符集一致性。

MySQL 没有 EXCEPT,得用 LEFT JOIN 模拟
MySQL 直到 8.0.31 仍不支持 EXCEPT(或 MINUS),所以想取「表 A 有、表 B 没有」的数据,不能直接写 SELECT * FROM t1 EXCEPT SELECT * FROM t2。必须靠 LEFT JOIN + IS NULL 组合来等价实现。
LEFT JOIN 模拟 EXCEPT 的写法要点
核心逻辑是:把待查的「差异行」作为左表,右表按主键或全字段关联,再筛出右表匹配不到的记录。
- 必须明确指定连接条件,字段名、类型、NULL 处理要完全一致;比如
a.id = b.id AND a.name = b.name,不能只连id就完事——否则语义就变成「主键相同但其他字段不同」,不是全行比对 - 所有参与比对的字段都要出现在
ON子句中,否则WHERE b.xxx IS NULL判定会失效 - 如果字段允许 NULL,
a.col = b.col在任一端为 NULL 时结果恒为UNKNOWN,导致漏匹配;此时应改用(a.col b.col)(MySQL 的 NULL 安全等于) - 性能上,连接字段最好都有索引;若无联合索引,多字段
ON条件可能触发全表扫描
示例(查 orders_new 中存在但 orders_old 中不存在的订单):
SELECT n.* FROM orders_new n LEFT JOIN orders_old o ON n.order_id = o.order_id AND n.customer_id o.customer_id AND n.amount o.amount AND n.status o.status WHERE o.order_id IS NULL;
为什么不用 NOT IN?
NOT IN 看似简洁,但实际风险高:
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- 只要
subquery返回任意一个NULL,整个NOT IN结果恒为FALSE或UNKNOWN,导致零行返回(静默失败) - 无法利用索引加速,尤其子查询结果大时性能断崖式下降
- 语义上它检查的是单字段值是否不在集合中,不适用于整行比对场景
容易被忽略的细节:字符集与排序规则
两个表字段即使名字和类型一样,若字符集(CHARACTER SET)或排序规则(COLLATE)不同,JOIN 或 = 比较可能隐式转换失败或结果异常。执行前务必确认:
SHOW CREATE TABLE table1; SHOW CREATE TABLE table2;
重点核对输出中各字段的 COLLATE 是否一致。不一致时,要么 ALTER TABLE ... CONVERT TO 统一,要么在 ON 条件里显式加 COLLATE,例如:a.name COLLATE utf8mb4_0900_as_cs = b.name COLLATE utf8mb4_0900_as_cs。










