执行计划中出现table scan或clustered index scan即表示发生全表扫描;是否合理需结合表大小、返回行数比例、过滤条件sargability及统计信息状态综合判断。

怎么看执行计划里有没有全表扫描
SQL Server 中,Table Scan 或 Clustered Index Scan 出现在执行计划里,基本就等于发生了全表扫描——哪怕表只有几千行,只要优化器没走索引查找(Index Seek)或范围查找(Index Seek + Predicate),而是从头读到尾,就算。
关键不是“有没有 Scan”,而是“Scan 是否必要”:比如 WHERE 条件用了非 SARGable 表达式(如 WHERE UPPER(name) = 'ABC')、缺少对应索引、或者统计信息过期,都可能把本该 Index Seek 的操作退化成 Clustered Index Scan。
在存储过程中抓取真实执行计划的实操方法
不能只看 SSMS 里“显示估计的执行计划”(Ctrl+L),那只是预估,不反映参数嗅探和实际数据分布。必须运行时捕获实际执行计划:
- 在 SSMS 中开启实际执行计划(
Ctrl+M),再执行存储过程(带具体参数值) - 用
SET STATISTICS XML ON包裹调用,然后从消息结果中提取 XML 计划(适合自动化分析) - 查询
sys.dm_exec_query_stats+sys.dm_exec_sql_text+sys.dm_exec_query_plan,筛选出该存储过程最近几次执行的计划缓存(注意:需有 VIEW SERVER STATE 权限)
查缓存时,用 object_name 或 text 字段匹配你的存储过程名,再用 query_plan 列解析 XML,搜索 <relop logicalop="Table Scan"> 或 <code>LogicalOp="Clustered Index Scan" 节点。
识别哪些 Scan 是真问题,哪些可接受
不是所有 Scan 都该被干掉。以下情况 Scan 可能合理:
- 表极小(
sys.dm_db_partition_stats.row_count ),走 Seek 反而更重 - 查询要返回 >80% 行数,优化器判断 Scan 成本更低
- WHERE 条件是
SELECT * FROM t WHERE 1=1这类无过滤逻辑
真正危险的是:大表(百万级以上)+ 小结果集(Clustered Index Scan。这时要检查:
- WHERE 字段是否在索引键前列(不是包含列)
- 是否存在隐式转换(如
WHERE id = '123',而id是INT) - 参数是否被优化器“嗅探”到了低选择性值,导致计划复用错误(可用
OPTION (RECOMPILE)验证)
快速定位存储过程内哪句 SQL 引发了 Scan
一个存储过程可能含多条语句,但执行计划 XML 默认只展示最外层操作。想精确定位,得拆解:
- 把存储过程里的每条 SELECT/UPDATE/DELETE 单独拎出来,加
SET STATISTICS XML ON执行 - 用
sys.dm_exec_procedure_stats查该过程总耗时,再结合sys.dm_exec_query_stats按plan_handle分组,找单次 CPU/IO 最高的语句 - 在执行计划 XML 中,按
StatementText属性反查原始 SQL 片段(注意:XML 中的StatementText是截断的,优先看StatementId和嵌套层级)
特别注意游标(DECLARE cursor_name CURSOR)或临时表(#tmp)后的查询——它们的执行计划独立生成,且统计信息为空白,极易触发意外 Scan。










