【问题标题】:sp_executesql is slow with parameterssp_executesql 带参数很慢
【发布时间】:2012-06-07 13:59:20
【问题描述】:

我使用 dapper-dot-net 作为 ORM,它会生成以下执行缓慢 (1700ms) 的 SQL 代码。

exec sp_executesql N'SELECT TOP 5 SensorValue FROM "Values" 
   WHERE DeviceId IN (@id1,@id2) AND SensorId = @sensor 
       AND SensorValue != -32768 AND SensorValue != -32767',N'@id1 
           bigint,@id2 bigint,@sensor int',@id1=139,@id2=726,@sensor=178

当我通过删除参数来修改此代码时,查询的执行速度非常快(20 毫秒)。缺少这些参数是否真的会产生如此大的差异?为什么?

exec sp_executesql N'SELECT TOP 5 SensorValue FROM "Values" 
   WHERE DeviceId IN (139,726) AND SensorId = 178 
       AND SensorValue != -32768 AND SensorValue != -32767'

【问题讨论】:

  • 我回答了类似的问题here

标签: sql-server dapper


【解决方案1】:

在末尾添加 OPTION(重新编译)

... AND SensorValue != -32767 OPTION (RECOMPILE) 

我怀疑您正在经历“参数嗅探”

如果是这种情况,我们可以将其保留为 OPTION 或考虑替代方案

更新 1

下面的文章将为你介绍“参数嗅探”http://pratchev.blogspot.be/2007/08/parameter-sniffing.html

我建议您了解其中的来龙去脉,因为这将使您更好地理解 sql server 内部结构(可能会咬人)。

如果您理解它,您就会知道如果语句经常执行非常,则与选项重新编译的权衡可能会降低性能。

我个人添加选项 recompile after 我知道根本原因是参数嗅探,除非存在性能问题,否则将其保留。重写语句以避免错误的参数嗅探会导致意图丢失,这会降低可维护性。但在某些情况下,重写是合理的(这样做时使用好的 cmets)。

更新 2

关于这个主题我读过的最好的书是在第 32 章,叫做 “参数嗅探:你最好的朋友......除非它不是由 GRANT FRITCHEY 提供的”

推荐。

SQL Server MVP Deep Dives, Volume 2

【讨论】:

  • 当然,与索引有关。
  • 这似乎已经成功了,我是否必须在每次执行时都保留这个,或者只保留一次才能重新编译它? (我做了一些额外的测试后会接受你的答案,谢谢)
  • "除非存在性能问题,否则我个人会将选项重新编译留在我的 sql 中。"您的意思是您通常 使用选项重新编译,除非存在性能问题?我认为默认情况下始终使用 OPTION RECOMPILE 不是一个好习惯(不要介意建议),除非它会导致问题。
  • @AaronBertrand 很高兴再次收到您的来信。一旦我诊断出根本原因是嗅探,我将添加选项重新编译,进行一些压力测试,如果没有性能下降,我将保留选项重新编译。如果重新编译 问题,我将使用已知技术(你记得)重写它,这确实会使语句通常失去其意图并使其不易维护。如果这是您的想法,我不会从选项重新编译开始。执行计划缓存一般来说是件好事。
  • @buckley 感谢您的澄清。我会在答案中澄清这一点。我把它理解为“我并不总是写查询,但当我这样做时,我会使用 OPTION RECOMPILE。”
【解决方案2】:

我最近遇到了同样的问题。我做的第一件事是在 where 语句的列上添加一个非聚集覆盖索引。

这改善了 SQL 的执行时间,但是当 dapper 执行查询时它仍然很慢,实际上它正在超时。

然后我意识到 dapper 生成的查询将参数作为 nvarchar(4000) 传递,因为我的 db 表列是 varchar(80),这导致它执行索引扫描而不是搜索 (如果这对您没有意义,我建议您阅读索引。)。意识到这一点后,我将我的 dapper where 语句更新为:

WHERE Reference = convert(varchar(80),@Reference)

使用上面的 where 语句执行索引查找,并提高了 100% 的性能。

添加:选项(重新编译)对我不起作用。

在所有这些歌曲和舞蹈之后,有一种方法可以告诉 dapper 默认为您执行此操作:

Dapper.SqlMapper.AddTypeMap(typeof(string), System.Data.DbType.AnsiString);

默认情况下,这会将任何字符串参数映射到 varchar(4000) 而不是 nvarchar(4000)。如果确实需要 Unicode 字符串比较,则可以显式地对参数进行转换。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-04-13
    • 2023-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多