您现在的位置: 365建站网 > 365文章 > SQLMINUS 就是 EXCEPT?去重规则、左结合优先级与 NOT EXISTS 取舍

SQLMINUS 就是 EXCEPT?去重规则、左结合优先级与 NOT EXISTS 取舍

文章来源:365jz.com 点击数:10 更新时间:2024-08-10 08:35

想找"A 表有、B 表没有"的记录,Oracle 用户会写 MINUS,其他人写 EXCEPT。这两个词其实是同一个集合运算的两种拼写,但"重复行怎么处理""和 LIMIT 谁先算"这两点写错,结果会静悄悄地不对。本文依据 PostgreSQL 官方集合运算文档原文整理。

SQL集合差集

一、三种集合运算与标准名字

PostgreSQL 7.4 节 "Combining Queries (UNION, INTERSECT, EXCEPT)"(实测 200)把三类运算并列给出:UNION(并)、INTERSECT(交)、EXCEPT(差)。要点:标准 SQL 与 PostgreSQL 用的是 EXCEPT,MINUS 是 Oracle 的私有写法。把 Oracle 脚本迁到 PG/MySQL 时 MINUS 会直接报语法错误,必须替换成 EXCEPT。

二、重复行:默认去重,除非写 ALL

官方原文对三种运算的公共行为说明为:"Again, duplicates are eliminated unless ALL is specified." 也就是说 EXCEPT 等价于差集后去重,EXCEPT ALL 则按重数相减。这个区别不是可有可无的细节:

  • EXCEPT:结果是无重复的行集合,只要右表出现过就整体排除。
  • EXCEPT ALL:左表某值出现 3 次、右表出现 1 次,结果仍保留 2 次。做数据对账(比如核对两张表的行数差异)时必须用 ALL,否则差异被去重掩盖。

参与运算的两个查询必须列数相同、对应列类型兼容,结果列名取第一个查询的别名。

三、结合律与优先级:最容易出错的一处

官方文档写明这些运算"associate left-to-right",即从左到右依次结合。所以 A EXCEPT B INTERSECT C 不等于 A 减去 (B 交 C),而是先算 A 减 B 再与 C 取交。混合使用不同优先级直觉(算术里乘优先于加)在集合运算中不成立,需要顺序就必须加括号。

文档给出的 LIMIT 例子极为典型,原文两个写法:SELECT a FROM b UNION SELECT x FROM y LIMIT 10 与 (SELECT a FROM b UNION SELECT x FROM y) LIMIT 10。前者 LIMIT 只作用于第二个 SELECT,后者才作用于合并结果。不加括号时子句绑定到最近的那个查询块,这是"为什么只取到一半数据"的根因。ORDER BY 同理,只能出现在最后一个查询块、且作用于整个结果集。

四、性能:EXCEPT 与 NOT EXISTS 怎么选

语义等价的两条写法,执行计划常不同:

SELECT id FROM a EXCEPT SELECT id FROM b
SELECT DISTINCT a.id FROM a WHERE NOT EXISTS (SELECT 1 FROM b WHERE b.id = a.id)

前者要先把两侧结果集物化再排序或哈希去重;后者可借助 b 上的索引做半连接探查,并且能顺手带出 a 的其他列。经验判据:只比较单列、两侧数据量适中时 EXCEPT 直观且够用;需要从大表中筛出差异行并返回多列时,NOT EXISTS 更省。大表上两种写法都建议先看执行计划,确认是否走了哈希聚合或索引。

五、按差集删除的写法

找到差异行后常要删掉。PostgreSQL 支持 DELETE ... USING 做连接删除,官方文档给出的例子形如 DELETE FROM films USING producers WHERE producer_id = producers.id AND ...,还有配合临时清单的批量删除写法 DELETE FROM user_logs AS dl USING delete_batch AS del ...。另外 DELETE FROM tasks WHERE status = 'DONE' RETURNING *; 能在删除的同时返回被删行,用于留痕与回滚校验非常实用。注意不带 WHERE 的 DELETE FROM films; 是全表清空但逐行删除,比 TRUNCATE 慢且仍写大量 WAL。

六、常见问题 FAQ

问:MySQL 支持 EXCEPT 吗?
答:8.0.31 起才支持,更早版本要用 LEFT JOIN ... IS NULL 或 NOT EXISTS 代替。

问:为什么差集结果比预期少?
答:先查两点——是否漏了 ALL 导致被去重,以及 ORDER BY/LIMIT 是否被绑定到了单个查询块。

问:EXCEPT 能比较包含 NULL 的列吗?
答:能,集合运算中 NULL 之间按"相等"处理,这与 WHERE 里 = NULL 恒为未知的行为不同。

七、总结

关于 MINUS/EXCEPT 的可靠结论是:EXCEPT 是标准写法、MINUS 仅 Oracle 可用,迁移必须替换;默认消除重复行,做数据对账要用 EXCEPT ALL;集合运算从左到右结合,与算术优先级直觉不同;LIMIT/ORDER BY 不加括号时只作用于最后一个查询块,这是最常见的静默错误;返回多列或需利用索引时改 NOT EXISTS,删除差异行可用 DELETE ... USING 并配合 RETURNING 留痕。结果数量不符预期时,优先怀疑 ALL 与括号,而不是数据库。

生产内容,同时打通搜索引擎(SEO)与AI搜索引擎(GEO)流量入口的软件! 点击进入365建站器 »