MySQL EXCEPT 怎样排除另一查询的结果行
要从查询 A 中排除查询 B 已有的结果行,可以把两条查询写成 A EXCEPT B。比较单位是两侧选出的完整行,默认去重;需要保留重复次数差时显式使用 EXCEPT ALL。左右交换会改变含义,EXCEPT 也不会删除原表数据。
本文以 MySQL 8.4 为说明环境;该语法从 MySQL 8.0.31 开始支持,不能只看“8.0”就判断可用。官方入口:https://dev.mysql.com/doc/refman/8.4/en/except.html。下面用“同一租户的待导出编号减去已交付编号”说明写法,示例为原创推演,未在本文环境执行。
先保护查询范围:差集不等于权限过滤
在设计导出名单时,我会先把要保护的对象写下来:候选编号、所属租户,以及排除依据。这样看 SQL 时,注意力不会只停留在 EXCEPT 这个关键字上。它只负责差集,不能自动替应用补上访问范围。
例如某租户的候选编号中有 102,如果右侧查询把其他租户的 102 也拿来比较,当前租户的候选就可能被错误排除。这个问题首先影响结果完整性;如果左侧本身漏了范围约束,差集也不会阻止越权数据进入结果。租户值应来自已授权的服务端上下文,并通过驱动参数绑定传入。
这里的风险分级按后果理解:漏掉左侧范围可能涉及数据暴露,漏掉右侧范围可能造成误排除,重复语义选错则会改变导出数量。本文只围绕这条差集查询处理这些边界。
沿着列投影找出误排除的路径
EXCEPT 不会根据列名推测主键,也不会自动找关联键。左右列数必须一致,对应位置应使用一致的数据类型和业务含义。如果左侧选出 customer_id, status,右侧也选出这两列,那么编号相同而状态不同的行仍是不同元组。只想按编号排除,就让两侧都只投影编号。

我倾向于在这里逐列写出字段,避免使用 SELECT *:表结构增加字段后,参与比较的内容可能随之变化。最终列名来自第一个查询块;对应位置类型不同时 MySQL 会综合两侧决定结果类型,因此也不要依靠隐式转换修正业务口径。字符串比较还需要关注列的字符集与排序规则。
用独立样例明确 DISTINCT 与 ALL
先用只读 CTE 构造一组可理解的数据。左侧 102 出现三次,右侧出现一次;左侧还有 101、103,右侧另有 104。下面每一条 SQL 都是独立语句,不创建或修改业务表。
-- 唯一名单:右侧出现过的编号从左侧集合中排除
WITH candidate AS (
SELECT 101 AS customer_id UNION ALL
SELECT 102 UNION ALL SELECT 102 UNION ALL SELECT 102 UNION ALL
SELECT 103
), delivered AS (
SELECT 102 AS customer_id UNION ALL SELECT 104
)
SELECT customer_id FROM candidate
EXCEPT DISTINCT
SELECT customer_id FROM delivered
ORDER BY customer_id;
按照集合语义,预期只剩 101、103。即使左侧有多条 102,DISTINCT 也不会留下其中两条;右侧出现过该值,就足以从唯一名单中排除它。省略 DISTINCT 是同一个含义。
-- 次数差:同值在左侧的次数减去右侧次数,最少为零
WITH candidate AS (
SELECT 101 AS customer_id UNION ALL
SELECT 102 UNION ALL SELECT 102 UNION ALL SELECT 102 UNION ALL
SELECT 103
), delivered AS (
SELECT 102 AS customer_id UNION ALL SELECT 104
)
SELECT customer_id FROM candidate
EXCEPT ALL
SELECT customer_id FROM delivered
ORDER BY customer_id;
这条的预期序列为 101、102、102、103:102 剩余两次。一般可把某个元组的剩余次数理解为 max(左侧次数 - 右侧次数, 0)。ALL 适合要保留多重集合次数的任务,不等于“右侧有这个编号也保留左侧全部重复行”。如果记录各有独立业务身份,应把真实身份列纳入契约,而不是指望重复次数替代身份。
把租户范围与比较列写进查询
回到导出场景,假定两个查询来源都包含 tenant_id、customer_id,交付来源还有 delivery_status。下面只生成唯一客户编号,不携带姓名等额外信息。问号是数据库驱动的预处理参数,占位值在应用中绑定,不能原样当作已填值 SQL 粘贴到普通客户端运行。
-- 两个参数分别绑定同一授权租户,不能由任意请求字段替代授权判断 SELECT customer_id FROM export_candidate WHERE tenant_id = ? EXCEPT DISTINCT SELECT customer_id FROM delivery_record WHERE tenant_id = ? AND delivery_status = 'delivered' ORDER BY customer_id;
两侧都限定租户,右侧还明确“已交付”的状态。把所有历史状态都纳入右侧,会把未成功交付的编号也排除。若导出对象其实是“客户 + 活动”组合,两侧就都应选出这两个字段,并在最终排序中使用相同的结果字段。

最终 ORDER BY 控制差集结果的显示顺序;不能依赖原表顺序。如果要对某一侧单独做 ORDER BY、LIMIT,需要用括号明确查询块范围。先给左侧加 LIMIT 再做差集,表达的是“先截取候选再排除”,并不保证最终还能得到同样数量的行。
给结果留下可追溯的口径
我会把这条查询的口径与导出任务一起记录:授权租户、候选范围、右侧状态条件、比较列、DISTINCT 或 ALL、执行时间以及结果数量。这里的“审计”是保留业务解释,不是把原始名单和敏感字段无差别写进日志。
如果事后需要重现同一结果,仅存 SQL 文本还不够:源数据可能已经变化。应在任务设计中说明结果文件或快照的保存策略,避免把两次不同时间的查询误认为同一数据状态。也不要宣称 EXCEPT 必然更快;数据量、重复分布及实际执行计划都会影响成本。
语法与版本依据分别见 MySQL EXCEPT 官方说明、集合查询的列、排序与组合规则和 8.0.31 发布说明。本文的租户导出场景与样例数据为原创设计。
使用前逐项确认结果含义
- 服务器版本支持 EXCEPT,不能把所有 MySQL 8.0 版本视为相同。
- 左侧是要保留的候选,右侧是排除依据,两侧范围已分别明确。
- 比较列数量、位置、类型与业务含义一致,额外说明字段没有混入比较键。
- 唯一名单选择 DISTINCT;确实需要次数相减才使用 ALL。
- 最终结果有明确排序,LIMIT 的位置与业务要求相符。
为什么同一个编号还是留在结果里?
先看两侧是否还选了其他列。EXCEPT 比较完整元组,相同编号搭配不同状态,并不是相同行。然后再看右侧条件是否实际覆盖了目标编号。
EXCEPT 会删除候选表中的行吗?
不会。本文的语句返回查询结果,原始行仍在来源表中。不要把得到差集名单误当成完成了删除或状态更新。
反过来写能得到同样结果吗?
不能保证。A EXCEPT B 与 B EXCEPT A 表达不同方向,右侧独有的 104 就是本例中的直观区别。
多个集合运算混用时怎样表达清楚?
用括号写清业务分组。MySQL 的 INTERSECT 优先于 UNION 和 EXCEPT,不应把不带括号的长表达式按自然阅读顺序猜测。
Go HTTP Server 为什么读取请求体后响应变慢
- 上一篇
- Go HTTP Server 为什么读取请求体后响应变慢
- 下一篇
- MySQL INTERSECT 怎样找出两组查询的共同记录
-
- 数据库 · MySQL | 38分钟前 |
- MySQL LOCK INSTANCE FOR BACKUP 怎样协调备份窗口
- 163浏览 收藏
-
- 数据库 · MySQL | 41分钟前 |
- MySQL CTE 怎样写递归组织结构查询
- 164浏览 收藏
-
- 数据库 · MySQL | 43分钟前 |
- MySQL JSON_OVERLAPS 怎样判断两个 JSON 是否有交集
- 341浏览 收藏
-
- 数据库 · MySQL | 44分钟前 |
- MySQL JSON_SCHEMA_VALIDATION_REPORT 怎样定位校验失败字段
- 391浏览 收藏
-
- 数据库 · MySQL | 48分钟前 |
- MySQL TABLE 语句怎样直接读取整张表
- 260浏览 收藏
-
- 数据库 · MySQL | 51分钟前 | MySQL · 数据库 · SQL · mysql INTERSECT 集合查询 INTERSECT ALL
- MySQL INTERSECT 怎样找出两组查询的共同记录
- 323浏览 收藏
-
- 数据库 · MySQL | 10小时前 | MySQL ·
- MySQL 外键迁移的检查开关与恢复顺序
- 318浏览 收藏
-
- 数据库 · MySQL | 19小时前 |
- MySQL 事件调度器怎样安全执行周期任务
- 159浏览 收藏
-
- 数据库 · MySQL | 19小时前 |
- MySQL histogram 怎样辅助没有合适索引的列估算
- 386浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 718次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 724次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 801次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 761次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 594次使用
-
- MySQL JSON_TABLE 展开数组时如何保留缺失字段
- 2026-09-10 501浏览
-
- MySQL 分区表怎么处理跨分区唯一键:分区列约束与建表取舍
- 2026-08-30 501浏览
-
- MySQL权限管理设置全攻略
- 2026-03-29 501浏览
-
- MySQL分片实现方法及常见方案解析
- 2025-06-24 501浏览
-
- MySQL表空间碎片怎么清理?超详细优化教程
- 2025-06-13 501浏览

