MySQL 不支持 INTERSECT 和 EXCEPT,需用 INNER JOIN + DISTINCT 模拟交集、NOT EXISTS 模拟差集,并用 COALESCE 处理 NULL;UNION ALL + GROUP BY 方案性能差且兼容性差。
INTERSECT 和 EXCEPT 在 MySQL 中根本不能用
MySQL 8.0.31 之前完全不支持 INTERSECT 和 EXCEPT,哪怕升级到 8.0.31+,也仅在 UNION 级别语法中“名义支持”,实际执行会报错:ERROR 1235 (42000): This version of MySQL doesn't yet support 'SELECT ... INTERSECT'。PostgreSQL、SQL Server、Oracle 原生支持,但写法细节有差异——比如 PostgreSQL 要求列数和类型严格一致,SQL Server 允许隐式转换但会警告。
所以跨数据库写法的第一原则是:别直接依赖这两个关键字。真要兼容 MySQL,得用 INNER JOIN + GROUP BY 模拟交集,用 LEFT JOIN ... WHERE ... IS NULL 模拟差集。
用 INNER JOIN 替代 INTERSECT(去重交集)
INTERSECT 默认去重,且要求字段结构相同。用 JOIN 模拟时,必须显式去重并处理空值。
- 如果两个查询结果都有主键或唯一组合字段,优先用
INNER JOIN+ON a.id = b.id - 若无明确关联字段,用所有列做等值连接,例如:
INNER JOIN t2 ON t1.col1 = t2.col1 AND t1.col2 = t2.col2 - 必须加
DISTINCT或GROUP BY,否则重复行会被放大(JOIN 本身不自动去重) - NULL 值无法参与等值判断,
INTERSECT把两个 NULL 视为相等,但=不行,需额外用IS NOT DISTINCT FROM(PostgreSQL 支持)或改用COALESCE(col, '##NULL##')临时替代(注意选一个业务中绝不会出现的占位符)
示例(兼容 MySQL/PG/SQL Server):
SELECT DISTINCT t1.* FROM table_a t1 INNER JOIN table_b t2 ON COALESCE(t1.name, '') = COALESCE(t2.name, '') AND COALESCE(t1.age, -1) = COALESCE(t2.age, -1);
用 LEFT JOIN + IS NULL 替代 EXCEPT(A 减 B)
EXCEPT 返回在左表存在、右表不存在的行,同样默认去重。用 LEFT JOIN 实现时,关键点不在连接逻辑,而在「如何定义‘不存在’」。