not in 在视图中强制物化子查询、受 null 值影响致结果为空、难以利用索引、参数化时性能雪崩;not exists 则支持关联执行、免 null 干扰、可走索引、适配参数化。

NOT IN 在视图里强制物化子查询结果集
视图本身不存储数据,但数据库优化器对 NOT IN 的处理倾向是:先完整执行子查询,把结果集“物化”(Materialize)成临时结构,再逐行比对主表。哪怕子查询只返回几条记录,这个物化动作仍会发生;而视图定义一旦固化,每次调用都重复这套流程,无法跳过。相比之下,NOT EXISTS 是关联式执行——它依赖主表当前行的值去驱动子查询,优化器更可能将其重写为 Hash Anti Join 或索引嵌套循环,跳过大量无效扫描。
视图中 NULL 值会彻底阻断 NOT IN 逻辑
只要子查询结果里出现任意一个 NULL,整个 NOT IN 条件就判定为 UNKNOWN,最终返回空结果集。这在视图里尤其危险:你无法控制上游数据是否含 NULL,也无法在视图定义里加 WHERE col IS NOT NULL 过滤(否则视图语义已变)。而 NOT EXISTS 完全不受 NULL 影响,它的语义是“找不到匹配行”,和空值无关。
视图 + NOT IN 极难利用索引
多数数据库(如 PostgreSQL、SQL Server)对视图中的 NOT IN 子句几乎放弃索引推导。执行计划里常见 Seq Scan + Materialize 组合,主表和子查询表都全表扫。即使子查询字段上有索引,也大概率被忽略。而 NOT EXISTS 在视图中仍能触发索引查找——只要关联条件字段有索引,优化器通常会用上。
参数化视图会让 NOT IN 性能雪崩
- 如果视图接受参数(比如通过
WHERE过滤传入),NOT IN子查询每次都要重新执行并物化,无法复用中间结果 - 参数值变化导致执行计划缓存失效,进一步放大开销
- 而
NOT EXISTS的关联执行天然适配参数,优化器更容易复用已编译的计划
真正麻烦的是:视图封装掩盖了这些行为。你看到的是一条简洁的 SELECT * FROM my_view,背后却在默默做全表扫描+物化+NULL校验。不看执行计划,根本意识不到问题出在哪。










