except和minus本质相同,均返回左表有而右表无的行,但except是sql标准及postgresql/sql server/sqlite语法,minus为oracle独有,mysql均不支持。

EXCEPT 和 MINUS 的本质区别在哪
它们干的是同一件事:返回左表有、右表没有的行,但不是所有数据库都支持同一个关键字。EXCEPT 是标准 SQL(SQL:2003 起)和 PostgreSQL、SQL Server、SQLite 的写法;MINUS 是 Oracle 独有的关键字,MySQL 完全不支持这两个操作符。
关键点在于:两个查询的列数、数据类型、顺序必须严格一致,否则直接报错,比如 ORA-01789: 查询块具有不正确的结果列数(Oracle)或 ERROR: each EXCEPT query must have the same number of columns(PostgreSQL)。
- 别指望用
EXCEPT比较带NULL的字段——NULL = NULL为UNKNOWN,所以两行仅在某列为NULL时仍可能被当作“相同”而剔除 - 结果默认去重,即使 A 表里某行重复 3 次、B 表里完全没出现,最终也只返回 1 次
- 想保留重复?得用
NOT EXISTS或LEFT JOIN ... IS NULL替代
用 EXCEPT 查 A 有 B 没有的记录(PostgreSQL / SQL Server)
假设 A 表是 orders,B 表是 shipped_orders,都含 order_id 和 customer_id:
SELECT order_id, customer_id FROM orders EXCEPT SELECT order_id, customer_id FROM shipped_orders;
注意:EXCEPT 不保证顺序,如需按时间倒序,必须在外层加 ORDER BY:
SELECT * FROM ( SELECT order_id, customer_id FROM orders EXCEPT SELECT order_id, customer_id FROM shipped_orders ) AS diff ORDER BY order_id DESC;
- 不能在
EXCEPT左右任一侧直接用ORDER BY(除非配合LIMIT或子查询) - SQL Server 中等价写法是
EXCEPT,不是MINUS;写成MINUS会报Incorrect syntax near 'MINUS' - 如果列名不同但语义相同(比如 A 表叫
cust_id,B 表叫customer_id),必须显式用别名对齐,否则列顺序错位会导致逻辑错误
Oracle 里只能用 MINUS,且大小写敏感
Oracle 不认 EXCEPT,必须写 MINUS。它对字符串默认区分大小写,这点常被忽略:
SELECT UPPER(customer_name) FROM customers MINUS SELECT UPPER(customer_name) FROM blacklisted;
如果不统一大小写,'Alice' 和 'alice' 会被当成两条不同记录,导致误判“存在差异”。
-
MINUS会自动按所有列排序后再去重,所以结果天然有序(但依赖 Oracle 默认排序规则,不可靠) - Oracle 不支持
MINUS ALL(即保留重复),要实现得靠ROW_NUMBER()配合NOT EXISTS - 若某列是
CLOB类型,MINUS会直接报错:ORA-00932: inconsistent datatypes—— 得先转成VARCHAR2(4000)截断处理
当 EXCEPT/MINUS 不可用时,用 LEFT JOIN 替代最稳
MySQL 用户没得选,得用 LEFT JOIN ... IS NULL。同样查“在 orders 但不在 shipped_orders”:
SELECT o.order_id, o.customer_id FROM orders o LEFT JOIN shipped_orders s ON o.order_id = s.order_id WHERE s.order_id IS NULL;
这个写法兼容所有主流数据库,且能控制是否去重(加 DISTINCT)和是否保留重复(去掉 DISTINCT)。
- JOIN 条件必须覆盖全部用于判断“存在性”的字段,比如要按
(order_id, status)判断,就得写ON o.order_id = s.order_id AND o.status = s.status - 如果 B 表有索引(如
(order_id)),LEFT JOIN性能通常优于子查询NOT IN(尤其 B 表含NULL时,NOT IN整个结果为空) - 别图省事用
NOT IN (SELECT ...)—— 只要子查询里任意一个order_id是NULL,整个查询就返回空集
实际用哪个,取决于你连的是什么库、字段有没有 NULL、要不要保留重复、以及 B 表数据量大不大。MINUS 和 EXCEPT 看着简洁,但限制多、容错低;LEFT JOIN 写起来略长,但可控性强,出问题也容易定位。










