【问题标题】:Why does sp_executesql run slower when parameters are passed as arguments为什么当参数作为参数传递时 sp_executesql 运行速度较慢
【发布时间】:2011-03-25 21:04:04
【问题描述】:

查询1:(闪电般的速度)

sp_executesql "select * from tablesView where Id = 1"

对比

查询2:(太慢了)

sp_executesql "select * from tablesView where Id = @Id", N"@Id int", @Id=1

tablesView - a view containing multiple joins

LINQ 总是将查询转换为 Query2 形式,因此性能非常糟糕。

问题:我需要 query2 缓慢的原因,以及任何解决方案(如果有的话)。 以及针对 LINQ 的解决方案。

----额外的cmets:

性能下降肯定是因为 2 列正在使用排名函数(row_number),但我无法避免它们,我需要它们。

【问题讨论】:

  • 你有很多 ID = 1 的行吗?
  • @Lasse,即使我有 50 条记录,差异也很大。比如 0 秒 vs 10 秒,一件事是 select * from table,通常是一个有很多连接的视图。
  • @id 传入的数据类型是什么?您可能有一个隐式强制转换正在阻止使用索引。
  • @WhoIsNinja - 嗯,没错。那么你可能有一个参数嗅探问题。参数化计划将根据第一次为@id 传入的值进行编译,这可能比 1 更具(或更少)选择性。为该值编制的计划可能不适合其他人。
  • @WhoIsNinja - 通常的解决方案是使用查询提示,例如 OPTIMIZE FORRECOMPILE。虽然不确定如何从 LINQ 执行此操作。

标签: .net sql sql-server linq


【解决方案1】:

我将在这里冒险并假设您有很多 ID = 1 的行。

如果不是,请纠正我。

SQL Server 处理查询速度缓慢的一个可能原因是它查看查询并执行以下操作:

嗯,我想知道他会为那个参数传递什么。
会是1吗?我在哪里有数以千计的行?
或者可能是 1742 年,我只有 3 个
我只是不知道,我最好做一个表扫描,以确保生成一个涵盖我所有基础的执行计划

如果列或列集的选择性低(即唯一值的数量远小于行数),SQL Server 有时会恢复为 tablescan 或类似的方式,只是为了确定性地获取所有行.

至少这是我的经验。特别是在对具有时间限制的数据的表进行日期范围选择时,我看到了相同的行为,执行WHERE dt <= @dt AND dt >= @dt 以获取@dt 在该行的一段时间内的所有行,恢复为表扫描,然后当我将实际日期作为文字放入 SQL 时,它的运行速度要快得多。

这里的问题是选择性,SQL Server 在为你的语句构建执行计划时不知道如何最好地满足所有场景,所以它会尝试猜测。

尝试添加查询提示以指定参数的典型值,即:

sp_executesql "select * from tablesView where Id = @Id option (optimize for (@id = 1742))", N"@Id int", @Id=1

【讨论】:

  • 优化没有帮助,而且我不能要求 linq 在它构建的查询中添加优化。 :(
  • 说到记录,如果这个查询中没有where子句,将返回百万条记录,因为视图包含巨大表之间的连接
  • 1.谁说过“No where 条款”?和 2. 您是否尝试使用 optimize for 子句执行 SQL 并查看差异?和 3) ... 是一个不知道你的数据的 ORM 仍然是最好的方法吗?
  • 使用 ORM 并不总是一个好的选择的另一个原因。
【解决方案2】:

这可能是参数嗅探问题。尝试包括以下行:

OPTION (RECOMPILE)

在您的 SQL 查询结束时。

这里有一篇文章解释什么是参数嗅探:http://blogs.technet.com/b/mdegre/archive/2012/03/19/what-is-parameter-sniffing.aspx

【讨论】:

  • 虽然我不能直接使用它,但它是我问题的答案,即参数导致表扫描而不是使用索引。使用 OPTION (RECOMPILE) 确实强制底层视图使用使用表索引的计划。不幸的是,我无法使用它,因为我的 SQL 中间件解析器无法接受该语法。我的解决方法是构建没有参数的 SQL,有效地将值硬编码到查询中。不理想,但它避免了将查询变成 sp_executesql。
【解决方案3】:
  1. 避免使用 SELECT *
  2. ADO.NET 3.5 在 Linq 1=TINYINT 2345=SMALLINT 76357242=INT 中有“参数查询”。 在 ADO.NET 4.0 中,参数查询被替换为默认的 INT 数据类型 1=INT, 2335=INT ,76357242=INT)

【讨论】:

  • 我从不使用 select * ,这只是举例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-21
  • 2019-11-13
  • 2015-11-03
  • 2018-05-06
  • 2023-03-23
相关资源
最近更新 更多