【问题标题】:Prevent ADO.NET from using sp_executesql防止 ADO.NET 使用 sp_executesql
【发布时间】:2011-01-03 17:32:56
【问题描述】:

在我们的 SQL Server 2005 数据库中(使用 Management Studio 和 DBCC FREEPROCCACHEDBCC DROPCLEANBUFFERS),下面的语句很快(编译时间~0.2s,执行时间~0.1s):

SELECT ... FROM ... WHERE a = 1 AND b = '' ...

但是,以下语句很慢(编译时间约为 0.2 秒,执行时间 7-11 秒):

exec sp_executesql N'SELECT ... FROM ... WHERE a = @a AND b = @b ...', N'@a int, @b nvarchar(4000), ...', @a=1, @b=N'', ...

SQL Server 选择不同的执行计划,尽管查询是相同的。这是有道理的,因为在第一种情况下,SQL Server 具有ab 和所有其他可用参数的实际值,并且可以使用统计信息来创建更好的计划。显然,针对参数具体值的查询计划比通用查询计划好得多,而且绝对超过任何“查询计划缓存”的性能优势。

现在我的问题是:ADO.NET 在执行参数化查询时似乎总是使用第二个选项 (sp_executesql),这 通常 是有意义的(查询计划缓存等)。然而,在我们的例子中,这会影响性能。那么,有没有办法呢

  • 强制 ADO.NET 使用不同于 sp_executesql 的东西(即 SQL Server 查询分析器将实际参数值考虑在内的东西)或
  • 强制 SQL Server 重新计算传递给 sp_executesql 的 SQL 的查询计划考虑参数值

请不要告诉我我必须回到丑陋、古老、​​危险的sql = "WHERE b = " + quoteAndEscape(parameterB)...

将 SQL 放入存储过程中没有区别(慢,有和没有 WITH RECOMPILE)。我没有发布实际的 SQL 语句,因为它非常复杂(连接多个表,包括子 SELECT 和聚合)。

【问题讨论】:

  • 将其转换为存储过程与即席查询一样慢?这是一个非常奇怪的行为,所以我必须问:你确定吗?
  • @Rubens - 你为什么这么说?与缓存、编译、参数化的即席查询相比,存储过程有哪些性能优势?
  • 是的,我确定。为什么应该更快?与执行时间 (7-11s) 相比,编译时间 (0.2s) 微不足道。
  • @David, @Heinzi:我这么说是因为查询执行计划(QEP)缓存;
  • @Rubens - 你也得到了即席查询,不是吗?

标签: sql-server ado.net


【解决方案1】:

我知道的旧线程,但我只是通过谷歌搜索几乎完全相同的短语找到了它!我遇到了完全相同的问题(查询在 Management Studio 中使用参数运行得非常快,但通过 ADO.Net 运行速度非常慢)并通过“exec sp_execute”在 Management Studio 中运行查询来复制问题。这两个执行计划非常不同,即使使用了优化查询提示,所以我所做的只是将一些数据初始选择到一个临时表中。这似乎有所不同,并且鉴于您说您的查询是一个复杂的查询,它也很可能对您的情况产生影响 - 我不太确定它是如何工作的,但它似乎将执行计划踢进了即使使用 sp_execute 也行。

【讨论】:

  • 这实际上正是我们最终所做的:使用临时表 (#...) 将查询拆分为较小的查询,以防止查询优化器选择非常糟糕的计划。
  • 当你只是在sql server中执行查询时它与sp_execute不同,sp_execute在sql server中缓存执行计划而不考虑任何参数。所以也许你的缓存执行计划对于第一个参数来说已经足够好了已经应用了,但是对于其他的不是最优的。为了确保你必须为当前查询清除缓存计划。这种问题是众所周知的参数嗅探。
【解决方案2】:

你可以试试OPTIMIZE FOR query hint which (quote):

指示查询优化器使用 局部变量的特定值 当查询被编译并且 优化。该值仅用于 在查询优化期间,而不是 在查询执行期间。优化 可以抵消参数检测 优化器的行为或可以是 创建计划指南时使用

【讨论】:

    【解决方案3】:

    我认为问题与在数据库中使用 VARCHAR 数据类型有关。如果 where 参数声明为 NVARCHAR,SQL Server 似乎不会使用指定的索引。

    但是,您可以将数据库列更改为 NVARCHAR(这当然会增加大小),然后索引性能可能会提高。

    我目前在使用 LINQ 时遇到了这个问题,实际上可能需要恢复使用存储过程来解决它。

    问题在this Microsoft Connect discussion中有详细说明

    【讨论】:

    • 经过数小时的阅读、嗅探参数问题、设置 ARITHABORT 参数等...这恰好是我的问题,我没有在查询中明确设置参数类型,它们正在被传递作为 nvarchar,我在数据库上有 varchar。谢谢
    【解决方案4】:

    我会将查询移至存储过程,然后在命令中指定 command.CommandType = CommandType.StoredProcedure。

    这不会创建 sp_executesql 并提高性能

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-12-02
      • 1970-01-01
      • 2012-12-10
      • 1970-01-01
      • 2012-07-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多