嵌套子查询在执行计划中不会标出“子查询”字样,而是被优化器重写为Nested Loops+Left Semi Join、Apply、Compute Scalar、Constant Scan或JOIN等算子,需通过算子类型、Outer References、数据流向及内外表依赖关系识别其实际物理实现。

嵌套子查询在执行计划里不会单独标出“子查询”字样,它会被优化器展开或重写为连接、Apply 或派生表,直接看图形计划里的算子类型和数据流向才是关键。
怎么识别嵌套子查询被重写成了什么
SQL Server 查询优化器对嵌套子查询没有保留原结构的义务——它会按代价选择最有效的物理实现。常见转化路径有:
- 相关子查询(含
WHERE中的EXISTS或IN)常转成Nested Loops+Left Semi Join或Apply算子 - 非相关子查询(如
SELECT (SELECT MAX(x) FROM t))可能提升为常量扫描或提前物化,图形计划中表现为独立的Compute Scalar或Constant Scan - 子查询返回单值且用于表达式(如
SELECT name, (SELECT COUNT(*) FROM orders o WHERE o.uid = u.id) cnt),大概率生成Compute Scalar+Top+Nested Loops结构 - 若子查询被重写为
INNER JOIN或LEFT JOIN,图形计划里就只剩连接算子,原始子查询痕迹完全消失
为什么 EXISTS 子查询常对应 Nested Loops
因为 EXISTS 语义是“只要找到一行就停止”,这天然契合嵌套循环的短路特性:外部表每行触发一次内表查找,命中即退出,不扫全表。但要注意:
- 如果外部输入行数多(比如百万级),而内表又没走索引查找(
Index Seek),就会变成Nested Loops+Table Scan,性能雪崩 - 执行计划里若看到
Nested Loops下方是Index Scan或Clustered Index Scan,说明内表缺失匹配字段上的索引 - 检查
Estimated Number of Rows和Actual Number of Rows是否严重偏差——统计信息过期会导致优化器误判,强行选Nested Loops而非更合适的Hash Match
怎么看子查询是否真的“相关”
图形执行计划本身不标注“相关/非相关”,得结合算子依赖关系反推:
- 如果某个
Nested Loops的内侧输入(下方分支)里有Compute Scalar或Filter引用了外侧表的列(比如[u].[Id]),那就是相关子查询 - 若内侧输入完全独立(比如只依赖常量或参数),且上方有
Constant Scan或Parameter Table,基本是非相关子查询 - 右键点击任意算子 → “属性” → 查看
Outer References字段:有值表示它依赖外部行,是相关子查询的关键证据
真正容易被忽略的是:子查询是否被自动去关联(un-correlated)。哪怕写了 WHERE id IN (SELECT uid FROM t2 WHERE t2.status = t1.status),优化器也可能因统计信息或版本改进而改用哈希匹配,此时图形计划里根本看不到 Nested Loops。别只盯语法,以实际算子为准。

















