【发布时间】:2014-04-21 22:35:15
【问题描述】:
我有一个 sql 查询,其确切代码是在 C# 中生成的,并通过 ADO.Net 作为基于文本的 SqlCommand 传递。
查询看起来像这样:
SELECT TOP (@n)
a.ID,
a.Event_Type_ID as EventType,
a.Date_Created,
a.Meta_Data
FROM net.Activity a
LEFT JOIN net.vu_Network_Activity na WITH (NOEXPAND)
ON na.Member_ID = @memberId AND na.Activity_ID = a.ID
LEFT JOIN net.Member_Activity_Xref ma
ON ma.Member_ID = @memberId AND ma.Activity_ID = a.ID
WHERE
a.ID < @LatestId
AND (
Event_Type_ID IN(1,2,3))
OR
(
(na.Activity_ID IS NOT NULL OR ma.Activity_ID IS NOT NULL)
AND
Event_Type_ID IN(4,5,6)
)
)
ORDER BY a.ID DESC
此查询已经运行良好一段时间了。它利用了我们在这些表上的一些索引。
无论如何,这个查询突然开始运行非常缓慢,但在 SSMS 中几乎是瞬间运行。
最终,在阅读了一些资源后,我能够验证我们得到的减速是由于参数嗅探不佳造成的。
通过将所有参数复制到局部变量,我能够成功地减少问题。问题是,这对我来说感觉有点不对劲。
我假设发生的事情是其中一个表的统计信息被更新,然后运气不好,第一次重新编译这个查询时,它被调用的参数值导致执行计划不同?
我能够在活动监视器中跟踪查询,导致查询在约 13 秒内运行的执行计划是:
在 SSMS 中运行会产生以下执行计划(并且只需要大约 100 毫秒):
那么问题是什么?
我想我的问题是:如何解决这个问题,而不将参数复制到局部变量,could lead to a large number of cached execution plans?
引用自linked comment / Jes Borland:
您可以在存储过程中使用局部变量来“避免”参数嗅探。但是,请理解,这可能会导致许多计划存储在缓存中。这可能有其自身的性能影响。没有一个万能的解决方案!
我的想法是,如果我有某种方法可以手动从临时数据库中删除当前的执行计划,那可能就足够了......但我在网上找到的所有内容都只向我展示了如何为实际命名的存储过程。
这是一个来自C#的基于文本的SqlCommand,所以我不知道如何找到缓存的执行计划,以及嗅探到的参数值,并将其删除?
注意:“只创建一个适当的存储过程”这个有点明显的解决方案很难做到,因为这个查询可以通过多种不同的方式生成......并且需要进行一些不愉快的重构。
【问题讨论】:
-
net.activity里面有member_id吗?
-
@TonyHopkinson 不,它没有。为什么?
-
本可以将 @member_id 上的两个左连接换成 members.member_id 少一个参数来嗅探,尽管我怀疑 Top(@n) 是给你带来了最严重的问题,再看一遍。跨度>
标签: sql sql-server sql-server-2008 tsql stored-procedures