【发布时间】:2017-11-03 10:25:11
【问题描述】:
我正在使用实体框架进行相当大的查询。最近,由于超时异常,此查询失败。
当我开始调查这个问题时,我使用了 LinqPad 并直接在 SSMS 中复制 SQL 输出并运行查询。此查询在 1 秒内返回!
然后查询看起来像(仅用于说明,实际查询要大得多)
DECLARE @p__linq__0 DateTime2 = '2017-10-01 00:00:00.0000000'
DECLARE @p__linq__1 DateTime2 = '2017-10-31 00:00:00.0000000'
SELECT
[Project8].[Volgnummer] AS [Volgnummer],
[Project8].[FkKlant] AS [FkKlant],
-- rest omitted for brevity
现在我使用 SQL Profiler 来捕获发送到服务器的真实 SQL。该查询与此查询完全相同,不同之处在于该查询封装在对sp_executesql 的调用中。像这样:
exec sp_executesql N'SELECT
[Project8].[Volgnummer] AS [Volgnummer],
[Project8].[FkKlant] AS [FkKlant],
-- rest omitted for brevity
',N'@p__linq__0 datetime2(7),@p__linq__1 datetime2(7)',
@p__linq__0='2017-10-01 00:00:00',@p__linq__1='2017-10-31 00:00:00'
当我在 SSMS 中复制/粘贴此查询时,它会运行 60 秒,因此在使用默认设置的 EF 时会导致超时!
我无法理解为什么会出现这种差异,因为这是同一个查询,唯一的问题是它的执行方式不同。
我阅读了很多关于 EF 为何使用 sp_executesql 的内容,并且我理解其中的原因。我还读到 sp_executesql 与 EXEC 不同,因为它使用了查询计划缓存,但我不明白为什么 SQL 优化器在为 sp_executesql 版本创建高性能查询计划时遇到如此困难,而它却能够创建高性能查询计划对于直接查询版本。
我不确定完整的查询本身是否会增加问题。如果是这样,请告诉我,我会进行编辑。
【问题讨论】:
-
您从 SSMS 执行了不同的查询。当您使用变量时,它们不会被嗅探,因此您的选择会在 WHERE 子句中执行 FOR UNKNOWN 值。当您使用 sp_executesql 时,参数值会被嗅探,并且您会根据 WHERE 子句中列的统计信息获取执行计划
-
添加提示选项(优化未知)
-
@sepupic,添加统计信息会解决问题吗?添加统计信息还有其他影响吗?
-
SQL Server 可以为参数化查询创建最佳计划,但最佳计划可能取决于提供的参数值。似乎为不适合当前日期的值创建了缓存计划。考虑
OPTIMIZE FOR UNKNOWN或RECOMPILE提示。 -
可能有很多原因,即缓存计划、您对变量的使用、设置选项等。看看:sommarskog.se/query-plan-mysteries.html。它很长,但我想你会在那里找到答案。
标签: sql sql-server entity-framework