当临时表使用查找和书签查找时,为什么表变量强制索引扫描?


18

我试图理解为什么使用表变量会阻止优化器使用索引查找,然后使用书签查找和索引扫描。

填充表格:

CREATE TABLE dbo.Test 
(
    RowKey INT NOT NULL PRIMARY KEY, 
    SecondColumn CHAR(1) NOT NULL DEFAULT 'x',
    ForeignKey INT NOT NULL 
) 

INSERT dbo.Test 
(
    RowKey, 
    ForeignKey
) 
SELECT TOP 1000000 
    ROW_NUMBER() OVER (ORDER BY (SELECT 0)),
    ABS(CHECKSUM(NEWID()) % 10)     
FROM sys.all_objects s1
CROSS JOIN sys.all_objects s2 

CREATE INDEX ix_Test_1 ON dbo.Test (ForeignKey) 

用单个记录填充表变量,并尝试通过搜索外键列来查找主键和第二列:

DECLARE @Keys TABLE (RowKey INT NOT NULL) 

INSERT @Keys (RowKey) VALUES (10)

SELECT 
    t.RowKey,
    t.SecondColumn
FROM
    dbo.Test t 
INNER JOIN 
    @Keys k
ON
    t.ForeignKey = k.RowKey

下面是执行计划:

在此处输入图片说明

现在,使用临时表代替相同的查询:

CREATE TABLE #Keys (RowKey INT NOT NULL) 

INSERT #Keys (RowKey) VALUES (10) 

SELECT 
    t.RowKey,
    t.SecondColumn
FROM
    dbo.Test t 
INNER JOIN 
    #Keys k
ON
    t.ForeignKey = k.RowKey

此查询计划使用查找和书签查找:

在此处输入图片说明

为什么优化器愿意用临时表而不是表变量来查找书签?

在此示例中,表变量用于表示通过存储过程中用户定义的表类型来的数据。

我知道,如果外键值发生数十万次,则索引搜索可能不合适。在这种情况下,扫描可能是一个更好的选择。对于我创建的场景,没有行的值为10。我仍然认为该行为很有趣,并且想知道是否有原因。

SQL小提琴

添加OPTION (RECOMPILE)并没有改变行为。UDDT有一个主键。

@@VERSION 是SQL Server 2008 R2(SP2)-10.50.4042.0(X64)(内部版本7601:Service Pack 1)(系统管理程序)

Answers:


15

出现这种情况的原因是,SQL Server无法确定与ForeignKey匹配的行数,因为没有以RowKey作为前导列的索引(它可以从#temp表的统计信息中推断出这一点,但是那些不能表变量/ UDTT的数据),因此它估计有100,000行,这比使用搜索+查找更好。到SQL Server意识到只有一行时,为时已晚。

您也许可以以不同的方式构造UDTT;在较新版本的SQL Server中,可以在表变量上创建二级索引,但是该语法在2008 R2中不可用。

顺便说一句,如果您尝试通过暗示嵌套循环联接来避免位图/探针,则可以得到搜寻行为(至少在我的有限试验中):

DECLARE @Keys TABLE (RowKey INT PRIMARY KEY); -- can't hurt

INSERT @Keys (RowKey) VALUES (10);

SELECT 
     t.RowKey
    ,t.SecondColumn
FROM
    dbo.Test t 
INNER JOIN 
    @Keys k
ON
    t.ForeignKey = k.RowKey
    OPTION (LOOP JOIN);

几年前,我从保罗·怀特那里学到了这个技巧。当然,在生产代码中放置任何类型的连接提示时,您应该小心-如果人们对基础对象进行更改并且特定类型的连接不再可能或不再是最佳状态,这可能会失败。

对于更复杂的查询,当您使用SQL Server 2012或更高版本时,跟踪标志2453可能会有所帮助。但是,该标志对这种简单的连接没有帮助。同样的免责声明也适用-这只是没有大量文档和严格的回归测试程序而通常不应该做的另一件事。

另外,Service Pack 1早已不受支持,您应该使用Service Pack 3 + MS15-058


3

表变量和临时表在许多方面都有不同的处理方式。在这里有一个很好的答案,其中有许多关于它们不同之处的细节。

具体来说,您可能会想到临时表可以生成其他统计信息和并行计划,而表变量具有更有限的统计信息(没有列级统计信息)并且没有并行计划这一事实。

在存储过程期间,最好将表变量转储到临时表中。

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.