为什么此查询不使用索引假脱机?


23

我问这个问题是为了更好地了解优化器的行为并了解索引假脱机的限制。假设我将1到10000之间的整数放入堆中:

CREATE TABLE X_10000 (ID INT NOT NULL);
truncate table X_10000;

INSERT INTO X_10000 WITH (TABLOCK)
SELECT TOP 10000 ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;

并强制嵌套循环加入MAXDOP 1

SELECT *
FROM X_10000 a
INNER JOIN X_10000 b ON a.ID = b.ID
OPTION (LOOP JOIN, MAXDOP 1);

对SQL Server采取的措施是相当不友好的。当两个表都没有任何相关索引时,嵌套循环联接通常不是一个好选择。这是计划:

错误的查询

该查询在我的计算机上花费了13秒,从表假脱机中提取了100000000行。但是,我不明白为什么查询必须很慢。查询优化器具有通过索引假脱机动态创建索引的能力。该查询似乎将是索引假脱机的理想选择。

以下查询返回与第一个查询相同的结果,具有索引假脱机,并且在不到一秒钟的时间内完成:

SELECT *
FROM X_10000 a
CROSS APPLY (SELECT TOP (9223372036854775807) b.ID FROM X_10000 b WHERE a.ID = b.ID) ca
OPTION (LOOP JOIN, MAXDOP 1);

解决方法1

此查询还具有索引假脱机,并且在不到一秒的时间内完成:

SELECT *
FROM X_10000 a
INNER JOIN X_10000 b ON a.ID >= b.ID AND a.ID <= b.ID
OPTION (LOOP JOIN, MAXDOP 1);

解决方法2

为什么原始查询没有索引假脱机?是否有任何已记录或未记录的提示或跟踪标志集将为其提供索引假脱机?我确实找到了这个相关的问题,但是它不能完全回答我的问题,因此我无法获得神秘的跟踪标志来处理该查询。

Answers:


20

如您所知,优化器的搜索并不详尽。它会尝试在上下文中有意义的事情,这些事情经常会在实际查询中带来收益。在两个单列未索引堆表之间强制循环联接不是这种情况。也就是说,这里有一些细节:

SQL Server喜欢转换的很早就应用于联接,因为它知道联接的更多技巧。稍后,它可能会探索将联接转换回应用程序。在两者之间的差相关联而参数(外部引用)。当内侧有合适的索引时,应用才有意义。您的示例没有索引,因此不能说服优化器探索将翻译应用到应用程序。

一个简单的(非应用)联接在联接运算符上具有联接谓词,而不是外部引用。对于非应用程序,后台处理优化通常是一个惰性表后台处理程序,因为内侧没有谓词,仅在连接处没有谓词。

优化器不会考虑动态建立索引以启用应用;相反,事件的顺序通常是相反的:因为存在良好的索引,所以可以应用变换。

您有时可以通过APPLY在查询中使用语法来鼓励应用而不是联接。未公开的跟踪标志9114可以通过阻止优化器将逻辑应用转换为预先加入的联接来帮助实现这一点。例如:

SELECT * 
FROM dbo.X_1000 AS a
CROSS APPLY (SELECT * FROM dbo.X_1000 AS b WHERE b.ID = a.ID) AS b
OPTION (QUERYTRACEON 9114);

线轴计划

最好使用索引假脱机,因为外部参考意味着选择应用于联接的内侧。您通常会通过SelToIndexOnTheFly其他方式看到它。请参阅我的文章《急切索引假脱机和优化程序》

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.