直接看执行计划中Actual Row Count与Estimated Row Count差异最准;SSMS中开启SET STATISTICS XML ON和IO ON后,在Execution plan页悬停JOIN算子即可查看,差异过大(如估100行、实8万行)表明统计信息过期。

直接看执行计划里的 Actual Row Count 和 Estimated Row Count 差异,比任何监控工具都准。
怎么在 SSMS 里实时看到 JOIN 的实际行数
打开 SSMS,执行前先开两个开关:
- SET STATISTICS XML ON —— 这会生成带详细算子信息的 XML 执行计划
- SET STATISTICS IO ON —— 同时捕获逻辑读、物理读,辅助判断 Nested Loops 是否在反复扫表
执行完后,在结果面板切换到「Execution plan」页签,把鼠标悬停在 JOIN 算子(比如 Nested Loops 或 Hash Match)上。你会立刻看到两行关键值:Estimated Row Count 和 Actual Row Count。如果后者比前者大一个数量级(比如估 100 行,实跑 8 万行),基本可以断定统计信息过期了,而不是 SQL 写得有问题。
为什么不能只依赖 Profiler 或 Extended Events 监控 JOIN
Profiler 的 SQL:StmtCompleted 事件只返回总耗时和扫描行数,不区分 JOIN 算子内部行为;Extended Events 虽然能捕获 query_post_execution_showplan,但默认不包含运行时统计(actual rows 是动态填充的),必须显式启用 query_plan_hash + query_plan_profile 选项,且仅限企业版。
- 免费方案就是用
SET STATISTICS XML ON+ 手动悬停,零配置、全版本支持 - 如果你在自动化脚本里需要解析 actual rows,别硬扒 XML——用
sys.dm_exec_query_stats关联sys.dm_exec_query_plan,从 cached plan 的<RelOp>节点里提取ActualRows属性更稳 - 注意:
SET STATISTICS XML本身有性能开销,线上只对慢查询临时开启,别长期开着
Hash Match 出现 Warning: Hash warning: No join predicate 怎么快速定位
这个警告不是性能问题,是语义错误——说明你漏写了 ON 条件,SQL Server 自动退化成笛卡尔积。它不会报错,但执行计划里会出现红色感叹号,且 Hash Match 算子的 EstimateRows 会显示为 2.1e+09 这类天文数字。
- 检查执行计划 XML,搜索
No join predicate字符串,定位到具体哪一层<RelOp PhysicalOp="Hash Match"> - 对应 SQL 中找形如
FROM a JOIN b但后面没ON的地方(常见于动态拼接 SQL 或模板漏填) - 别信“应该不会漏”,SQL Server 对缺失
ON的容忍度极高,反而更难发现
最常被忽略的是:执行计划里的 actual 统计只在首次执行时刷新,如果缓存了旧 plan,哪怕你刚 UPDATE STATISTICS,也得清掉 sys.dm_exec_cached_plans 里对应句柄,否则看到的还是过期数据。

















