我注意到,当tempdb事件大量溢出(导致查询缓慢)时,对于特定的联接,行估计常常会偏离。我已经看到溢出事件是通过合并和哈希联接发生的,它们通常会将运行时间增加3倍至10倍。这个问题涉及如何在减少溢漏事件机会的假设下改进行估计。
实际行数40k。
对于此查询,计划显示错误的行估计(11.3行):
select Value
from Oav.ValueArray
where ObjectId = (select convert(bigint, Value) NodeId
from Oav.ValueArray
where PropertyId = 3331
and ObjectId = 3540233
and Sequence = 2)
and PropertyId = 2840
option (recompile);
对于此查询,该计划显示了良好的行估计(56k行):
declare @a bigint = (select convert(bigint, Value) NodeId
from Oav.ValueArray
where PropertyId = 3331
and ObjectId = 3540233
and Sequence = 2);
select Value
from Oav.ValueArray
where ObjectId = @a
and PropertyId = 2840
option (recompile);
是否可以添加统计信息或提示来改善第一种情况的行估计?我尝试添加具有特定过滤器值(属性= 2840)的统计信息,但要么无法正确组合,要么被忽略,因为在编译时ObjectId未知,并且可能选择了所有ObjectId的平均值。
有什么模式可以先执行探针查询,然后使用该模式来确定行估计,还是必须盲目飞行?
这个特殊的属性在一些对象上具有许多值(40k),在绝大多数对象上具有零。我希望可以为给定的连接指定最大预期行数的提示而感到满意。这是一个普遍困扰的问题,因为某些参数可以作为连接的一部分动态确定,或者可以更好地放置在视图中(不支持变量)。
是否可以调整任何参数以最大程度地减少溢出到tempdb的机会(例如,每个查询的最小内存)?稳健的计划对估计没有影响。
编辑2013.11.06:对评论和其他信息的响应:
这是查询计划图像。警告是关于convert()的基数/搜索谓词的:
根据@Aaron Bertrand的评论,我尝试替换convert()作为测试:
create table Oav.SeekObject (
LookupId bigint not null primary key,
ObjectId bigint not null
);
insert into Oav.SeekObject (
LookupId, ObjectId
) VALUES (
1, 3540233
)
select Value
from Oav.ValueArray
where ObjectId = (select ObjectId
from Oav.SeekObject
where LookupId = 1)
and PropertyId = 2840
option (recompile);
作为一个奇怪但成功的兴趣点,它也使它可以缩短查找:
select Value
from Oav.ValueArray
where ObjectId = (select ObjectId
from Oav.ValueArray
where PropertyId = 2840
and ObjectId = 3540233
and Sequence = 2)
and PropertyId = 2840
option (recompile);
这两个都列出了正确的键查找,但是只有第一个列出了ObjectId的“输出”。我猜这表明第二个确实短路了吗?
有人可以验证是否曾经执行过单行探针以帮助进行行估计吗?当单行PK查找可以极大地提高直方图中查找的准确性时(特别是在存在泄漏可能性或历史记录的情况下),将优化限制为仅对直方图估计值似乎是错误的。当实际查询中有10个这些子联接时,理想情况下它们将并行发生。
附带说明,由于sql_variant将其基本类型(SQL_VARIANT_PROPERTY = BaseType)存储在字段本身内,所以我期望convert()几乎是无用的,只要它可以“直接”转换(例如,不能将字符串转换为十进制,而是将int转换为int或int到bigint)。由于这在编译时未知,但可能为用户所知,因此sql_variants的“ AssumeType(type,...)”函数将允许对它们进行更透明的处理。
declare @a bigint =
完成后使用拆分查询对我来说似乎是一个自然的解决方案,为什么这是不可接受的?
CONVERT()
在列中使用然后将它们加入。在大多数情况下,这当然不是很有效。在这个特定的值中,它只是要转换的一个值,因此这可能不是问题,但是表上有哪些索引?EAV设计通常只有通过适当的索引(通常在狭窄的表中有很多索引)才能表现良好。