2010-08-02 46 views
1

这个问题是一个后续到This Question为什么此查询超时? V2

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

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

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

Linq查询和执行的SQL有什么不同?

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

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

UPDATE:

当执行经由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 

UPDATE:

唯一的区别查询之间是LINQ的与sp_executesql的执行它和SSMS不,否则查询是相同的。

UPDATE:

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

回答

1

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

不幸的是,没有什么好的通用的方法(我知道的),以避免与L2S。 context.ExecuteCommand(“sp_recompile ...”)将是一个丑陋但可能的解决方法,如果查询不是非常频繁地执行。

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

将查询的部分(或全部)移动到视图*,函数*或存储过程* DB端将是另一个解决方法。
* = 在那里你可以使用本地PARAMS(FUNC/proc中)或优化器提示(三个),以迫使“好”的计划

顺便说一句,你有没有尝试更新所涉及的表的统计信息? SQL Server的自动更新统计信息并不总是能够完成这项工作,所以除非你有一个预定的工作来做这件事,否则它可能是值得考虑的脚本和调度更新统计数据......根据需要上下调整样本大小也可以帮帮我。

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

+0

好吧,当我添加执行计划告诉我的缺失索引时,问题就消失了。选择一个更好的计划,虽然不像非参数化查询那样并行,但它是亚秒级的响应时间。 – 2010-08-03 22:34:37

1

Linqpad为您提供的SQL可能不完全是发送到数据库的内容。

这里是我建议:

  1. 运行SQL事件探查器对DB同时执行查询。找到对应于您的查询的语句
  2. 将整个语句粘贴到SSMS中,并启用“显示实际执行计划”选项。
  3. 在此发布计划,供人们解剖。

关键的事情,寻找:

  • 表扫描,这通常意味着缺少一个索引
  • 宽箭头图形平面图,表明大量的中介的行处理中。

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

另外,你是否正在执行其他用户正在加载的数据库?

+0

当我查看分析器时,除了使用sp_executesql外,sql看起来完全相同。不,数据库目前并不承受沉重的负担,虽然它可能有时。我正在等待查询完成以获取实际的执行计划。 – 2010-08-02 21:38:28

+0

好吧,所以执行计划是非常不同的,即使查询是相同的。从SSMS执行的SQL查询使用了大量的并行操作,86%的工作在TransmittalDetail的Table Scan中,而更长的Linq执行计划在“Table Spool(Lazy Spool)”中花费了27%的时间,并且只有29在同一个表扫描中的时间百分比和在嵌套循环中的41%。在这两种情况下,都报告了相同的失踪指数。 – 2010-08-02 21:53:13

1

乍一看有很多连接,但我只能看到一件事情,就是立即减少数量,而无法在我面前显示模式......它看起来并不像您需要AccountSummary。

[t6].[AccountSummaryID] = @p3 

可能是

[t5].[AccountSummaryID] = @p3 

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

+0

有趣的是,你提到这一点。通过执行优化,查询将在25秒内返回,而不是4分钟。仍然比应该慢一个数量级,但这很好。 – 2010-08-03 01:36:20

+0

这会让我相信连接的数量会影响查询性能,这可能看起来像一个很大的“duh”,但类似的查询即使有很多连接也不会花费太长的时间。 – 2010-08-03 01:52:21

0

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

此外,当您通过SSMS工作时,使用的连接属性不同于从应用程序或从LinqPad执行查询时使用的连接属性。对SSMS连接的连接属性和应用程序的连接进行一些检查,你应该看到不同之处。所有其他的事情是平等的,这可能是不同的。请记住,您通过两种不同的应用程序执行查询,这些应用程序可以有两种不同的配置,甚至可以使用两种不同的数据库驱动程序。如果查询是相同的,那么这只是我可以看到的差异。

在旁注中,如果您手工制作SQL,则可以尝试将条件从WHERE子句移动到适当的JOIN子句中。这实际上改变了SQL Server执行查询的方式,并且可以产生更高效的执行计划。我已经看到了将筛选器从WHERE子句移动到JOIN中导致SQL Server在执行计划的前面过滤表的情况,并且显着改变了执行时间。

+0

隔离级别似乎没有任何区别。我将查询包含在与SMSS(ReadCommitted)相同的默认隔离级别的TransactionScope中,并且没有区别。另外请注意,当我在SMSS中执行sp_executesql查询时,它也需要很长时间,但非sp_executesql查询不会。我想有一个区别在于一个是参数化的,另一个不是。 – 2010-08-03 03:22:30

1

您确定要在此处使用LEFT OUTER JOIN吗?这个查询看起来应该可能使用INNER JOINs,尤其是因为您正在使用可能为NULL的列,然后对其进行区分。

+0

为了响应EchoCoder关于隔离级别的评论,我写了一篇关于在没有任何表锁的情况下执行LINQ查询的便捷方式的博客文章:http://ryancoder.blogspot.com/2010/06/execute-linq-queries-without -locking.html如果这给你一个很大的加速,它表明来自其他并发查询的表锁定是一个问题。但是这并不能解释LINQ和SSMS之间的差异 – RyanHennig 2010-08-03 02:50:06

+0

查询由Linq和LEFT OUTER JOINS生成,所以我不知道有什么方法可以改变它。使用你的扩展也不会产生任何明显的变化。反正这些表中不会有NULLS。 – 2010-08-03 03:19:31

+0

好吧,那么真正的问题在于,LINQ正在生成LEFT OUTER JOIN而不是INNER JOIN。检查你的DBML。您可能会遗失连接子句中使用的列之间的关联。还要确保任何作为主键的列在DBML中都被标记为这样。然后重新生成LINQ类并重试。这可能会给你一个巨大的加速。 – RyanHennig 2010-08-03 22:10:46