表变量有主键时对执行计划的影响

sam*_*hop 4 sql-server optimization table-variable

在阅读了大量有关 SQL Server 中临时表和表变量之间差异的信息后,我正在尝试从主要使用临时表切换到主要使用表变量。(它们似乎更适合我通常使用的查询类型。)

在这些查询中,表包含驱动查找过程的唯一标识符。在使用临时表时,我的习惯是包含一个PRIMARY KEY约束,以便查询优化器知道它不会看到任何重复项。但是,鉴于优化器(在大多数情况下,对于我的查询)假定表变量仅包含单行*,根据定义这是唯一的,如果存在PRIMARY KEY约束,查询优化器是否会做出任何不同的选择?

* 从技术上讲,它假定没有行,但将零替换为一。(因为零与估计过程的其余部分的交互非常差。)但这也取决于在编译查询时是否填充了表变量。这里有一些背景信息:SQL Server 中的临时表和表变量有什么区别?.

我目前正在使用 SQL Server 2014,但我很好奇新版本的行为是否发生变化。


正如已经指出的那样,PRIMARY KEY约束带有聚集索引,它为查询优化器提供了更多关于如何从表变量中获取数据的选择。我意识到了这一点,并考虑了查询计划的其余部分。但是在试图澄清我的问题之后,我决定我试图提出的问题太广泛了,可能特别针对我的极端情况。(不过是对半万亿行表的导航类型查询,期望达到亚秒级性能。)所以我将保持我的问题不变。

Pau*_*ite 7

...如果存在PRIMARY KEY约束,查询优化器是否会做出不同的选择?

是的,它可能会。估计一行(理解估计值可能不正确)与知道该表仅包含唯一值不同。例如,某些计划太空探索需要钥匙。

一个好的一般经验法则是向优化器提供尽可能多的关于数据和查询任务的信息。如果有钥匙,请明确说明。在大多数情况下,声明键不会增加很多成本(除了一点键盘工作)。

我个人很少使用表变量。与等效的临时表相比,缺少统计信息(包括分布和密度)和基数信息(所有单独的考虑因素)为优化器提供的信息更少。我的经验是,随着时间的推移,表变量计划不能很好地适应不断变化的环境。

我只在有特殊原因以确保从查询优化的角度来看它总是足够的时才使用表变量。只有你有足够的关于你的数据库和查询的信息来说明你的情况是否正确。

这个问题相当广泛(没有具体的例子),这个答案也是如此。


db2*_*db2 6

由于在表变量上声明 PRIMARY KEY 隐式地为键列创建了一个索引(实际上是 SQL Server 2014 之前索引表变量的唯一方法),它的存在肯定会对结果查询计划产生影响。优化器将在适当的情况下使用该主键索引。您可以通过在启用执行计划的情况下运行此简短脚本来查看实际情况 - 表扫描将更改为聚集索引搜索:

--No primary key/index
DECLARE @t1 TABLE (
    id int NOT NULL,
    data varchar(50) NOT NULL
)

INSERT INTO @t1 VALUES
(1, 'aaaaa'),
(2, 'bbbbb'),
(3, 'ccccc'),
(4, 'ddddd'),
(5, 'eeeee')

SELECT * FROM @t1 WHERE id = 4

--With primary key/index
DECLARE @t2 TABLE (
    id int NOT NULL PRIMARY KEY CLUSTERED,
    data varchar(50) NOT NULL
)

INSERT INTO @t2 VALUES
(1, 'aaaaa'),
(2, 'bbbbb'),
(3, 'ccccc'),
(4, 'ddddd'),
(5, 'eeeee')

SELECT * FROM @t2 WHERE id = 4
Run Code Online (Sandbox Code Playgroud)

现在,至于声明 PRIMARY KEY 而不是普通的 CLUSTERED INDEX(2014 年允许您这样做)是否会导致不同的查询计划?我不能权威地说。这个人为的测试仍然是一个聚集索引搜索:

DECLARE @t3 TABLE (
    id int NOT NULL,
    data varchar(50) NOT NULL,
    INDEX IX1 CLUSTERED (id)
)

INSERT INTO @t3 VALUES
(1, 'aaaaa'),
(2, 'bbbbb'),
(3, 'ccccc'),
(4, 'ddddd'),
(5, 'eeeee')

SELECT * FROM @t3 WHERE id = 4
Run Code Online (Sandbox Code Playgroud)

我怀疑在表变量上使用非聚集索引时事情会变得更加不确定,其中优化器需要估计潜在 RID 查找的成本并将它们与表扫描进行权衡。