为嵌套循环设置统计 I/O

The*_*war 8 sql-server execution-plan database-internals sql-server-2016

考虑以下查询:

CREATE PROC dbo.GetPage  @orderid  AS INT    = 0, -- anchor sort key
            @pagesize AS BIGINT = 25
 AS
SELECT
TOP (@pagesize) orderid, orderdate, custid, empid
 FROM dbo.Orders WHERE orderid > @orderid ORDER BY orderid;

exec GetPage 25,25
Run Code Online (Sandbox Code Playgroud)

上述查询的 SET STATISTICS IO 返回:

(25 row(s) affected)
Table 'Orders'. Scan count 1, logical reads 87, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Run Code Online (Sandbox Code Playgroud)

Itzik Ben-Gan 在他的书中对上述内容的解释是这样的:

执行查询计划所涉及的 I/O 成本由以下组成:

  • 查找索引的叶子:3 次读取(索引具有三个级别)。
  • 25 行的范围扫描:0-1 次读取(数百行适合一页)。
  • 用于优化查找的嵌套循环预取:9 次读取(通过使用跟踪标志 8744 禁用预取来衡量)
  • 25 次键查找:75 次读取

查询计划

执行计划

现在我的问题是,由于嵌套循环对从 seek 返回的每一行执行一次键查找,所以应该查找读取为 25*3 :75,与键查找相同吗?

查询计划 XML

CREATE PROC dbo.GetPage  @orderid  AS INT    = 0, -- anchor sort key
            @pagesize AS BIGINT = 25
 AS
SELECT
TOP (@pagesize) orderid, orderdate, custid, empid
 FROM dbo.Orders WHERE orderid > @orderid ORDER BY orderid;

exec GetPage 25,25
Run Code Online (Sandbox Code Playgroud)

Pau*_*ite 9

现在我的问题是,由于嵌套循环对从 seek 返回的每一行进行一次键查找,所以应该查找读取 25*3 :75 与键查找相同

如果问题是“搜索是否也需要 75 次读取?” 那么答案是否定的,原因是 Itzik 给出并在问题中引用:

寻找索引的叶子:3 次读取(索引有三级) 25 行的范围扫描:0-1 次读取(数百行适合一页)

寻找范围扫描起始位置的初始查找(在索引查找运算符中)需要 3 次读取。从那时起,存储引擎会记住扫描的当前位置,因此获取下一个 Index Seek 行需要零次或一次读取。如果下一行在同一页上,则读取为零;如果在下一页,请阅读。

行为的差异是混淆的常见来源,也是我不喜欢将逻辑读取作为性能指标的原因之一。