为什么表变量迫使索引扫描在临时表使用seek和书签查找时?
题
我正在尝试理解为什么使用表变量是防止优化器使用索引寻找,然后添加书签查找与索引扫描。
填充表:
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
.
此查询计划使用Seek和Bookmark查找:
为什么优化器愿意使用临时表进行书签查找,但不是表变量?
表变量在该示例中使用,以表示存储过程中通过用户定义的表类型的数据。
我意识到索引寻求可能不适合,如果外键值发生数十万次。在这种情况下,扫描可能是一个更好的选择。对于我创建的场景,没有一个值10的行。我仍然认为行为很有意思,并且想知道它是否有原因。
添加OPTION (RECOMPILE)
没有改变行为。 UDDT有一个主要键。
@@VERSION
是SQL Server 2008 R2(SP2) - 10.50.4042.0(x64)(构建7601:service pack 1)(虚拟机管理程序)
解决方案
行为的原因是SQL Server无法确定与ForkeKey匹配的行数量,因为没有索引RowKey作为前导列(它可以从#temp表上的统计信息中推断出来,但是那些表变量/ UDTTS不存在),因此它会估计为100,000行,比Seek +查找更好地处理。当SQL Server实现只有一行时,为时已晚。
你可能能够以不同的方式构建你的UDTT;在更现代版本的SQL Server中,您可以在表变量上创建辅助索引,但此语法在2008 R2中不可用。
如果您尝试通过暗示嵌套循环加入:,您可以获得Seek行为(至少在我的有限试验中)。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);
.
i 几年前从Paul White 那里学到了这个技巧。当然,如果人们对底层对象进行更改,并且不再可能或不再可能或不再最佳,则应注意在生产代码中的任何类型的加入提示 - 这可能会失败。
对于更复杂的查询,并且当您移动到SQL Server 2012或更高版本时,它可能是 trace标志2453 可以提供帮助。但是,这个旗帜没有帮助这个简单的连接。同样的免责声明将适用 - 这只是一个替代的事情,你通常不应该做的没有大量的文件和严格的回归测试程序。
此外,Service Pack 1是长时间的支持,您应该在 Service Pack 3 + ms15-058 。其他提示
表变量和临时表以多种方式不同地处理。有一个大在这里回答,有很多细节,以及它们不同的地方。
具体在您的情况下,我猜测临时表可以产生额外的统计数据和并行计划,而表变量具有更多有限的统计信息(无列级别统计),并且没有并行计划是您的罪魁祸首。
在存储过程的持续时间内,您可能会更好地将表变量转储到温度表中。