我正在尝试理解为什么使用表变量是防止优化器使用索引寻找,然后添加书签查找与索引扫描。

填充表:

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的行。我仍然认为行为很有意思,并且想知道它是否有原因。

sql fiddle

添加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

其他提示

表变量和临时表以多种方式不同地处理。有一个大在这里回答,有很多细节,以及它们不同的地方。

具体在您的情况下,我猜测临时表可以产生额外的统计数据和并行计划,而表变量具有更多有限的统计信息(无列级别统计),并且没有并行计划是您的罪魁祸首。

在存储过程的持续时间内,您可能会更好地将表变量转储到温度表中。

许可以下: CC-BY-SA归因
不隶属于 dba.stackexchange
scroll top