NOT IN在视图中性能更差,因其无法利用索引且遇NULL即失效;而NOT EXISTS通过索引查找、兼容NULL、支持条件下推和优化器重写,性能更优且更安全。

NOT IN 在视图中性能更差,核心原因就两条:它无法利用索引 + 遇到 NULL 就整体失效。
NOT IN 会强制全表扫描,根本用不上索引
数据库优化器对 NOT IN 子查询通常无法生成高效执行计划。即使内表的关联字段(比如 number_id)上有索引,NOT IN 仍大概率触发内外表双全表扫描 —— 因为它要确认“主表每一行的值都不在子查询结果集中”,而子查询结果集是动态生成、无序且不可预判边界的。
相比之下,NOT EXISTS 是关联驱动的:外层每取一行,就用该行的值去内表索引中做一次等值查找(index seek),只要内表有合适索引,就能跳过大量无关数据。
- 实测中,当
call_log表有 1000+ 行、number_id建了索引时,NOT EXISTS执行耗时约 0.8 秒;同条件下的NOT IN耗时飙升至 12 秒以上 - 视图定义一旦固化
NOT IN,后续所有基于该视图的查询都会继承这个低效计划,无法通过外层 WHERE 过滤提前剪枝
NULL 值会让 NOT IN 返回空结果,且无法被察觉
只要子查询返回的任意一行中,用于比较的列(如 SELECT number_id FROM call_log)包含 NULL,整个 NOT IN 条件就会判定为 UNKNOWN,最终结果集为空 —— 即使逻辑上本该返回几百条数据。
这在视图中尤其危险:你查视图时只看到“没数据”,却很难意识到是子查询某张基础表里混入了 NULL,而不是业务真的没匹配项。
-
NOT EXISTS对NULL完全免疫:它只关心“是否存在匹配行”,不参与值比较,所以不会因NULL失效 - 哪怕你加了
WHERE number_id IS NOT NULL,优化器也未必能将其下推进子查询,实际仍可能读取并过滤掉含NULL的行,徒增开销
视图场景下 NOT EXISTS 更易被优化器重写
现代数据库(如 SQL Server、PostgreSQL、MySQL 8.0+)在展开视图时,会对 NOT EXISTS 子句保留关联语义,允许与外层查询合并、下推条件、甚至转成 LEFT JOIN ... IS NULL 等更优形态。
而 NOT IN 在视图中基本被当作黑盒子查询处理,优化器很难安全地改写它 —— 毕竟它的语义依赖于完整结果集,稍作调整就可能改变 NULL 行为。
- 如果你在视图上再加
WHERE status = 'active',NOT EXISTS很可能把status条件下推到内表查询中;NOT IN则大概率先算完子查询,再在外层过滤 - 某些数据库还会对视图中的
NOT EXISTS自动添加索引提示(如INDEXhint),但对NOT IN几乎从不这么做
真正容易被忽略的是:视图的可维护性比单条 SQL 更高——一旦你在线上视图里用了 NOT IN,后续排查慢查询时,第一反应往往是“查哪张底表没建索引”,很少会怀疑视图定义本身就有结构性性能缺陷。


















