我有一张大约一张桌子。 135M行:
CREATE TABLE [LargeTable]
(
[ID] UNIQUEIDENTIFIER NOT NULL,
[ChildID] UNIQUEIDENTIFIER NOT NULL,
[ChildType] INT NOT NULL
)
它有一个非聚集索引,没有包含列:
CREATE NONCLUSTERED INDEX [LargeTable_ChildID_IX]
ON [LargeTable]
(
[ChildID] ASC
)
(它聚集在ID
)。
我想加入一个包含几千行的临时表:
CREATE TABLE #temp
(
ChildID UNIQUEIDENTIFIER PRIMARY KEY,
ChildType INT
)
...add #temp data...
SELECT lt.ChildID, lt.ChildType
FROM #temp t
INNER JOIN [LargeTable] lt
ON lt.[ChildID] = t.[ChildID]
但是查询计划包括对大表的索引扫描:
如果我更改索引以包含额外的列:
CREATE NONCLUSTERED INDEX [LargeTable_ChildID_IX] ON [LargeTable]
(
[ChildID] ASC
)
INCLUDE [ChildType]
然后查询计划变为更明智的事情:
所以我的问题是:为什么SQL Server在第一种情况下仍然不能使用索引查找,但是使用RID查找从非聚集索引到表数据?当然,这会比这么大的桌子上的索引扫描更有效吗?
答案 0 :(得分:1)
第一个查询计划实际上很有意义。请记住,SQL Server从不读取记录,而是读取页面。在您的表中,页面包含许多记录,因为这些记录非常小。
使用原始索引,如果将使用第二个查询计划,在查找索引中的所有RID并读取索引页面之后,需要读取聚簇索引中的页面以读取ChildType列。在最坏的情况下,这是它需要读取的每个记录的整个页面。由于每页有许多记录,这可能归结为读取聚集索引中的大部分页面。
SQL服务器根据统计数据猜测,只需扫描聚簇索引中的页面就需要更少的页面读取,因为它可以避免读取非聚集索引中的页面。
这里重要的是临时表中行的数量与大表中页面的数量相比较。假设在大表中随机分发ChildID,一旦临时表中的行数接近或取代大表中的页数,SQL服务器就必须读取大表中的每个页面。< / p>
答案 1 :(得分:0)
由于列ChildType
未包含在索引中,因此必须返回到聚簇索引(使用上面提到的行标识符查找)来获取ChildType
的值。<登记/>
当您在非聚簇索引中INCLUDE
此列时,它将被添加到可用于查询的索引的叶级别。
答案 2 :(得分:0)
通俗地称为'the index tipping point'。基本上,基于成本的优化器在什么时候认为执行扫描而不是搜索+查找更有效。通常是大小的20%左右,在您的情况下,这将基于来自#temp表统计的估计。 YMMV。
您已经有了答案:包含所需的列,使索引覆盖。