Eri*_*ing 15 sql-server execution-plan
在 SQL Server 中有几种假脱机。我感兴趣的两个是Table Spool s 和Index spools,在修改查询之外。
只读查询,特别是在嵌套循环连接的内侧,可以使用表或索引假脱机来潜在地减少 I/O 并提高查询性能。这些线轴可以是Eager或Lazy。就像你我一样。
我的问题是:
Joe*_*ish 12
这有点宽泛,但我想我理解真正的问题并会相应地回答。虽然只是要谈论表与索引假脱机。我认为将其视为表和索引假脱机之间的选择是不正确的。如您所知,可以在单个子树中获得索引假脱机、表假脱机或索引假脱机和表假脱机。我相信在以下情况下您会得到一个索引假脱机通常是正确的:
您可以通过简单的演示看到其中的大部分内容。首先创建一对堆:
DROP TABLE IF EXISTS dbo.X_10000_VARCHAR_901;
CREATE TABLE dbo.X_10000_VARCHAR_901 (ID VARCHAR(901) NOT NULL);
INSERT INTO dbo.X_10000_VARCHAR_901 WITH (TABLOCK)
SELECT TOP (10000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;
DROP TABLE IF EXISTS dbo.X_10000_VARCHAR_800;
CREATE TABLE dbo.X_10000_VARCHAR_800 (ID VARCHAR(800) NOT NULL);
INSERT INTO dbo.X_10000_VARCHAR_800 WITH (TABLOCK)
SELECT TOP (10000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;
Run Code Online (Sandbox Code Playgroud)
对于第一个查询,没有什么可寻求的:
SELECT *
FROM dbo.X_10000_VARCHAR_901 a
CROSS JOIN dbo.X_10000_VARCHAR_901 b
OPTION (MAXDOP 1);
Run Code Online (Sandbox Code Playgroud)
所以优化器没有理由将连接转换为应用。由于成本原因,您最终会得到一个表线轴。所以这个查询在第一次测试中失败了。
对于下一个查询,可以预期优化器有理由考虑应用:
SELECT *
FROM dbo.X_10000_VARCHAR_901 a
INNER JOIN dbo.X_10000_VARCHAR_901 b ON a.ID = b.ID
OPTION (LOOP JOIN, MAXDOP 1);
Run Code Online (Sandbox Code Playgroud)
但这并不意味着:
此查询未通过第二个测试。一个完整的解释是here。引用最相关的部分:
优化器不会考虑动态构建索引来启用应用;相反,事件的顺序通常是相反的:因为存在良好的索引而转换为应用。
我可以重写查询以鼓励优化器考虑应用:
SELECT *
FROM dbo.X_10000_VARCHAR_901 a
INNER JOIN dbo.X_10000_VARCHAR_901 b ON a.ID >= b.ID AND a.ID <= b.ID
OPTION (MAXDOP 1);
Run Code Online (Sandbox Code Playgroud)
但是仍然没有索引假脱机:
此查询未通过第三次测试。在 SQL Server 2014 中,索引键长度限制为 900 字节。这在 SQL Server 2016 中得到了扩展,但仅适用于非聚集索引。假脱机的索引是聚集索引,因此限制保持在 900 字节。在任何情况下,都不能应用索引假脱机规则,因为它可能导致查询执行期间出错。
将数据类型长度减少到 800 最终提供了一个带有索引假脱机的计划:
毫不奇怪,索引假脱机计划的成本比没有假脱机的计划便宜得多:89.7603 单位与 598.832 单位。您可以看到与未记录的QUERYRULEOFF BuildSpool
查询提示的区别:
这不是一个完整的答案,但希望这是您正在寻找的一些答案。