为什么此查询超时? V2

时间:2010-08-02 20:55:21

标签: asp.net performance linq-to-sql sql-server-2008 .net-3.5

此问题是This Question

的后续问题

解决方案,清除执行计划缓存似乎当时工作,但我一直在遇到同样的问题,清除缓存似乎不再有帮助。这里一定有更深层次的问题。

我发现如果从查询中删除.Distinct(),它会在大约2秒内返回行(带有重复项)。但是,使用.Distinct()需要4分钟才能完成。表中有很多行,而某些where子句字段没有索引。但是,返回的记录数量相当少(最多只有几十个)。

关于它的令人困惑的部分是,如果我通过Linqpad获取Linq查询生成的SQL,那么将该代码作为SQL执行,或者在SQL Management Studio(包括DISTINCT)中执行它在大约3秒内执行。

Linq查询与执行的SQL有什么区别?

我有一个短期的解决方法,那就是将没有.Distinct()的集合作为List返回,然后在列表中使用.Distinct,这需要大约2秒。但是,我不喜欢在Web服务器上运行SQL Server。

我想了解为什么在Linq中Distinct慢2个数量级,但不是SQL。

更新:

当通过Linq执行代码时,sql探查器显示此代码,这基本上是相同的查询。

sp_executesql N'SELECT DISTINCT [t5].[AccountGroupID], [t5].[AccountGroup] 
    AS [AccountGroup1]
FROM [dbo].[TransmittalDetail] AS [t0]
INNER JOIN [dbo].[TransmittalHeader] AS [t1] ON [t1].[TransmittalHeaderID] = 
    [t0].[TransmittalHeaderID]
INNER JOIN [dbo].[LineItem] AS [t2] ON [t2].[LineItemID] = [t0].[LineItemID]
LEFT OUTER JOIN [dbo].[AccountType] AS [t3] ON [t3].[AccountTypeID] = 
    [t2].[AccountTypeID]
LEFT OUTER JOIN [dbo].[AccountCategory] AS [t4] ON [t4].[AccountCategoryID] = 
    [t3].[AccountCategoryID]
LEFT OUTER JOIN [dbo].[AccountGroup] AS [t5] ON [t5].[AccountGroupID] = 
    [t4].[AccountGroupID]
LEFT OUTER JOIN [dbo].[AccountSummary] AS [t6] ON [t6].[AccountSummaryID] = 
    [t5].[AccountSummaryID]
WHERE ([t1].[TransmittalEntityID] = @p0) AND ([t1].[DateRangeBeginTimeID] = @p1) AND 
([t1].[ScenarioID] = @p2) AND ([t6].[AccountSummaryID] = @p3)',N'@p0 int,@p1 int,
   @p2 int,@p3 int',@p0=196,@p1=20100101,@p2=2,@p3=0

更新:

查询之间的唯一区别是Linq使用sp_executesql执行它而SSMS不执行它,否则查询是相同的。

更新:

我尝试了各种交易隔离级别无济于事。我还设置了ARITHABORT,试图在执行时强制重新编译,没有区别。

5 个答案:

答案 0 :(得分:1)

Linqpad提供的SQL可能并不完全是发送给数据库的内容。

以下是我的建议:

  1. 执行查询时,对数据库运行SQL事件探查器。找到与您的查询相对应的陈述
  2. 将整个参数粘贴到SSMS中,并启用“显示实际执行计划”选项。
  3. 将结果计划发布在此处供人们剖析。
  4. 要寻找的关键事项:

    • 表扫描,通常意味着缺少索引
    • 图形计划中的宽箭头,表示正在处理大量中间行。

    如果您使用的是SQL 2008,查看该计划通常会告诉您是否缺少哪些索引应该添加以加快查询速度。

    另外,您是针对其他用户负载的数据库执行的吗?

答案 1 :(得分:1)

乍一看有很多连接,但我只能看到一件事就是立即减少数字而没有我前面的架构......看起来你不需要AccountSummary。

[t6].[AccountSummaryID] = @p3

可能是

[t5].[AccountSummaryID] = @p3

返回值来自[t5]表。 [t6]仅对那个看起来像是从t5到t6的外键的参数使用过滤器,所以它出现在[t5]中。因此,您可以完全删除连接到[t6]。或者我错过了什么?

答案 2 :(得分:1)

你确定要在这里使用LEFT OUTER JOIN吗?此查询看起来应该使用INNER JOIN,尤其是因为您正在获取可能为NULL的列,然后对其执行不同的操作。

答案 3 :(得分:1)

糟糕的计划很可能是参数嗅探的结果:http://blogs.msdn.com/b/queryoptteam/archive/2006/03/31/565991.aspx

不幸的是,没有任何好的通用方式(我知道)可以避免使用L2S。如果查询没有经常执行, context.ExecuteCommand (“sp_recompile ...”)将是一个丑陋但可能的解决方法。

稍微更改查询以强制重新编译可能是另一个。

将查询的部分(或全部)移动到视图*,函数*或存储过程*数据库端将是另一种解决方法。
* = 您可以使用本地参数(func / proc)或优化器提示(全部三个)来强制执行“好”计划

顺便问一下,您是否尝试更新所涉及的表的统计信息? SQL Server的自动更新统计信息并不总能完成这项工作,因此除非您有预定的工作要做,否则可能值得考虑编写脚本并安排更新统计信息...... ...根据需要调整样本大小也可以帮助

可能有办法通过在所涉及的表上添加*(或删除*)正确的索引来解决问题,但不知道基础数据库模式,表大小,数据分布等有点难以再提供关于...的具体建议 * = 缺失和/或重叠/冗余索引都可能导致错误的执行计划。

答案 4 :(得分:0)

检查SSMS会话与应用程序之间是否具有相同的事务隔离级别。这是我见过的相同查询之间性能差异较大的罪魁祸首。

此外,在使用SSMS时,与从应用程序或LinqPad执行查询时相比,使用的连接属性也不同。对SSMS连接的Connection属性和应用程序的连接进行一些检查,您应该看到差异。在所有其他条件相同的情况下,这可能是不同的。请记住,您通过两个不同的应用程序执行查询,这两个应用程序可以有两种不同的配置,甚至可以使用两个不同的数据库驱动程序。如果查询相同,那么我只能看到差异。

另外,如果您是手工制作SQL,可以尝试将条件从WHERE子句移动到相应的JOIN子句中。这实际上改变了SQL Server执行查询的方式,并可以生成更高效的执行计划。我已经看到过这样的情况:将过滤器从WHERE子句移动到JOIN中导致SQL Server在执行计划的早期过滤表并显着改变了执行时间。