intersect自动去重并返回唯一公共行,要求列数、类型严格一致,null被视为相等,不支持保留重复行;mysql 8.0.33前需用join或in模拟。

INTERSECT 会自动去重,不能直接保留重复行
SQL 标准中 INTERSECT 的行为等价于 INTERSECT DISTINCT,它只返回同时出现在两个结果集中的**唯一行组合**。即使某行在左右查询中都出现多次,最终也只会出现一次。
如果你需要保留重复(比如“共同出现至少两次的记录”),INTERSECT 无能为力,得改用 JOIN + 计数逻辑,或者用 IN 配合子查询加 COUNT 聚合。
- 左右查询列数、列类型必须严格一致,否则报错:
ERROR: each INTERSECT query must have the same number of columns - 列名以左侧查询为准,右侧列名被忽略;别名只能在最外层加
- 不支持
ORDER BY在单个分支里写,排序必须放在整个INTERSECT语句末尾
用 INTERSECT 替代 INNER JOIN 时要注意 NULL 处理
INTERSECT 把 NULL = NULL 当作相等(即两个 NULL 被认为是同一值),而标准 INNER JOIN ON a.x = b.x 中,NULL = NULL 返回 UNKNOWN,该行不会被匹配出来。
这意味着:如果字段可能含 NULL,用 INTERSECT 得到的结果通常比等价的 JOIN 更多。
- 例如:
(SELECT 1 UNION SELECT NULL) INTERSECT (SELECT 1 UNION SELECT NULL)返回两行:1和NULL - 但
SELECT t1.x FROM t1 INNER JOIN t2 ON t1.x = t2.x会漏掉所有x IS NULL的匹配 - PostgreSQL 和 SQL Server 遵循此行为;MySQL 不支持
INTERSECT(8.0.33+ 才开始实验性支持)
性能上,INTERSECT 通常比 IN + 子查询更高效,但不如 EXISTS 稳定
数据库优化器一般把 INTERSECT 转成哈希交集或排序合并,对大结果集较友好。相比之下,WHERE x IN (SELECT ...) 容易触发关联执行或临时表膨胀。
不过,当左查询结果极小、右查询极大时,EXISTS 往往更快,因为它可短路——找到第一个匹配就停,而 INTERSECT 必须完整计算两边结果再比对。
- 推荐顺序:确定存在性 → 用
EXISTS;需取完整交集字段 → 用INTERSECT;仅判断单列值是否存在 →IN可读性更好 - 记得给参与列建索引,尤其右查询的
SELECT列 —— 多数引擎无法下推索引到INTERSECT内部 - 在 PostgreSQL 中,加
EXPLAIN ANALYZE能清楚看到是否用了HashSetOp Intersect
替代方案:没有 INTERSECT 的数据库(如 MySQL 5.7 或 SQLite)怎么模拟
SQLite 支持 INTERSECT,但 MySQL 直到 8.0.33 才作为实验室功能引入,且默认关闭。老版本只能手动模拟:
SELECT a.* FROM table_a a INNER JOIN table_b b ON a.id = b.id AND a.name = b.name AND a.status = b.status;
注意:这要求你明确写出所有列的等值条件,且无法处理 NULL(同前)。更通用的写法是:
SELECT * FROM table_a WHERE (id, name, status) IN ( SELECT id, name, status FROM table_b );
-
IN元组语法在 MySQL 8.0+ 和 PostgreSQL 中可用,SQLite 支持但有长度限制 - 若列含
NULL,(a,b) IN ((1,NULL))永远为FALSE,此时必须拆成IS NOT DISTINCT FROM(PG)或三值逻辑绕过(MySQL 用IS NULL显式判断) - 别忘了加
DISTINCT——IN不自动去重,可能多出重复行
实际写的时候,先确认目标数据库是否原生支持,再看数据里有没有 NULL,最后才决定要不要为性能微调写法。三个因素叠在一起,很容易漏掉一个。











