MINUS不是语法糖,而是Oracle专为集合差运算设计的高速通道,底层通过哈希去重+SET$DIFF算子直接判断存在性,跳过JOIN的笛卡尔积、NULL补位与隐式转换,执行更准更快。

MINUS 在 Oracle 中执行集合差时,天然绕过连接中间态,比 FULL OUTER JOIN 快得多——不是语法糖快,是执行引擎专为它开了高速通道。
MINUS 底层走哈希去重 + 归并,不生成笛卡尔积
当你写 SELECT a, b FROM t1 MINUS SELECT a, b FROM t2,Oracle 不会先做 JOIN 再筛 NULL,而是:
- 对两个结果集分别构建哈希表,自动去重
- 直接在哈希层面做“存在性判断”:t1 的每行查 t2 哈希表,没命中就保留
- 完全跳过传统 JOIN 必须经历的连接匹配、中间行膨胀、NULL 补位等步骤
常见错误现象:FULL OUTER JOIN 在千万级表上执行计划里常出现 NESTED LOOPS 或 SORT MERGE JOIN,而 MINUS 执行计划里稳定出现 HASH UNIQUE 和 SET$DIFF 算子,这是性能差异的根源。
FULL OUTER JOIN 被迫处理 NULL 语义和字段对齐,开销不可忽略
FULL OUTER JOIN 要同时保全左右两表所有行,就必须:
- 为缺失侧补 NULL 占位(哪怕你只关心“哪边缺”,也得先把整行拉出来)
- 对齐字段类型,隐式转换可能触发全表 cast
- 若关联字段含 NULL,ON a.id = b.id 会直接跳过这些行,你还得额外加 OR (a.id IS NULL AND b.id IS NULL) 逻辑,进一步拖慢
而 MINUS 天然按 SQL 标准处理 NULL:它不等于任何值(包括自身),所以 (1, NULL) 和 (1, NULL) 在集合意义上不相等,不会被减掉——这个行为确定、无需额外条件。
列数/顺序/类型匹配要求看似严,实则是性能保障
MINUS 强制要求两个 SELECT 返回列数、顺序、类型一致,这不是限制,是让优化器省去类型推导和字段映射的开销。对比之下:
- FULL OUTER JOIN 允许两边字段名不同,但优化器得做列绑定、别名解析、NULL 检查三重工作
- 如果你用 SELECT *,字段顺序稍有变动(比如加了新列),FULL OUTER JOIN 可能静默错位,而 MINUS 直接报错 ORA-01789: query block has incorrect number of result columns,反而帮你早发现问题
实操建议:永远显式写列名,别用 *;若字段类型不完全一致(如 VARCHAR2(50) vs VARCHAR2(100)),手动 CAST 统一,避免隐式转换吃掉哈希优势。
MySQL / PostgreSQL 用户注意:EXCEPT ≠ MINUS,行为有细微差别
PostgreSQL 的 EXCEPT 和 Oracle 的 MINUS 功能一致,但 MySQL 完全不支持;SQL Server 用 EXCEPT,语义相同。不过要注意:
- PostgreSQL EXCEPT 默认去重,和 MINUS 一样;但 EXCEPT ALL 保留重复,Oracle 没这个变体
- 所有数据库中,EXCEPT/MINUS 都要求列类型严格兼容,比如 NUMBER 和 CHAR 直接比较会报错,而 FULL OUTER JOIN 可能悄悄转成字符串再比,结果不可靠
最容易被忽略的一点:如果你要对比的字段本身允许 NULL(比如可为空的外键),MINUS 会把 (1, NULL) 和 (1, NULL) 当作两行不同元组——这不是 bug,是集合论定义决定的,你得接受它,而不是试图绕开。

















