INNER JOIN返回两表连接条件匹配的交集记录,只保留双方都存在的行;LEFT JOIN则以左表为基准返回全部记录,右表无匹配时填充NULL。

用 INNER JOIN 实现交集
交集就是两个表中都存在的记录,INNER JOIN 是最直接、最符合语义的做法。它只保留连接条件匹配的行,天然对应集合交集的定义。
常见错误是误用 LEFT JOIN 加 WHERE ... IS NOT NULL,虽然结果可能一致,但可读性差、执行计划未必优化,还容易漏掉 NULL 值处理逻辑。
- 确保连接字段类型一致(比如都是
INT或都为VARCHAR),否则隐式转换可能导致意外不匹配 - 若字段允许
NULL,INNER JOIN会自动跳过含NULL的行——这是正确行为,不是 bug - 示例:查用户表和订单表共有的用户 ID:
SELECT u.id FROM users u INNER JOIN orders o ON u.id = o.user_id;
用 LEFT JOIN + WHERE IS NULL 做差集(A − B)
要找在表 A 中存在、但在表 B 中不存在的记录,标准写法是 LEFT JOIN 后过滤右表为 NULL 的行。这不是“技巧”,而是 SQL 标准中表达差集的惯用模式。
容易踩的坑是忘记加 WHERE b.id IS NULL,或者把条件错写在 ON 子句里(比如 ON a.id = b.id AND b.id IS NULL),这会导致逻辑错误——ON 是连接条件,不是过滤条件。
-
LEFT JOIN必须以“被减数”表(A)为左表,“减数”表(B)为右表 - 判断
NULL的字段必须来自右表(B),且最好是主键或非空唯一字段,避免因多对一连接产生误判 - 示例:查有用户记录但从未下过单的用户
SELECT u.* FROM users u LEFT JOIN orders o ON u.id = o.user_id WHERE o.user_id IS NULL;
UNION ALL 和 EXCEPT/NOT EXISTS 的取舍
某些数据库(如 PostgreSQL、SQL Server)支持 EXCEPT,语法更接近数学集合运算;而 MySQL 不支持,得用 NOT EXISTS 替代。别硬套一种写法到所有环境。
EXCEPT 默认去重,NOT EXISTS 不去重,行为不等价;UNION ALL 在构造差集时毫无意义,纯属混淆概念。
- PostgreSQL 中可用:
SELECT id FROM users EXCEPT SELECT user_id FROM orders - MySQL 中等价写法是:
SELECT id FROM users u WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id) -
NOT EXISTS对索引友好,尤其当右表很大时,通常比LEFT JOIN+IS NULL更快
JOIN 条件里混用 AND 和 OR 容易出错
在 ON 子句里写多个条件时,AND 是安全的,但一旦出现 OR,就可能破坏差集/交集的语义。例如 ON a.x = b.x OR a.y = b.y 会让连接变得宽松,结果不再是严格意义上的集合运算。
如果业务真需要“任一字段匹配”,应该先用 UNION 拆成多个明确的子查询,再分别做交/差,而不是塞进一个 JOIN 的 ON 里。
- 复杂条件优先考虑 CTE 或子查询封装,保持每层语义单一
- 用
EXPLAIN看执行计划,确认是否真的走了索引;JOIN表太多或条件太松时,优化器可能放弃使用索引 - 别忽略 collation(排序规则)影响:字符串比较时,
utf8mb4_0900_as_cs和utf8mb4_general_ci可能导致相同值被判定为不等
实际写的时候,交集几乎总是用 INNER JOIN,差集优先选 LEFT JOIN + IS NULL(兼容性好)或 NOT EXISTS(性能敏感场景),至于 EXCEPT,只在明确知道目标库支持且团队熟悉时才用。最常被忽略的是字段类型和 NULL 处理——它们不会报错,但会让结果悄悄偏离预期。

















