【问题标题】:Entity Framework - extremely long execution time for stored procedure实体框架 - 存储过程的执行时间极长
【发布时间】:2017-03-09 16:35:45
【问题描述】:

我昨天正在跟进我的问题,Entity Framework 6 get complext return value from a stored procedure。我的存储过程现在在实体框架下运行。但是,3分钟后超时,连接超时。

我在我的 SQL Server Management Studio 中使用以下行运行存储过程(客户信息省略):

EXEC spGetDupWOs @ProjectName=N'...', @City=N'...', @State=N'LA', @ProposalNum=N'201703080740-001', @County=N'...', @Owner=N'...', @QuoteRecipients=N'...', @ProjectID=-1

它在不到一秒的时间内执行。当实体框架执行它时,它需要永远。

使用 SQL Server Profiler,我确定实体框架正在将此行发送到 SQL 服务器:

exec sp_executesql N'EXEC spGetDupWOs',N'@ProjectName nvarchar(19),@City nvarchar(6),@State nvarchar(2),@ProjectNum nvarchar(12),@County nvarchar(10),@Owner nvarchar(23),@QuoteRecipients nvarchar(23),@ProjectID bigint',@ProjectName=N'...',@City=N'Holden',@State=N'LA',@ProposalNum=N'201703080740-001',@County=N'Livingston',@Owner=N'...',@BID_RECIP=N'...',@ProjectID=-1

当我在 SSMS 中运行它时,它需要很长时间才能运行。

阅读类似的问题,问题似乎是参数嗅探和执行计划的变化。

这是我在应用程序中执行存储过程的调用:

            List<DuplicateProposals> duplicateCheckResults = 
                db.Database.SqlQuery<DuplicateProposals>("spGetDupWOs", 
                spl.ToArray())
                .ToList();

在网上看了一堆文章后,我更加困惑了。如何更改我的呼叫来解决此问题?

【问题讨论】:

  • 您是否尝试过修复参数嗅探场景?
  • 就是这样。我被我读到的东西弄糊涂了,我不知道怎么读。我需要更改我的存储过程或我的应用程序调用吗?
  • 更改 SP。例如:CREATE PROC Thing(@param1 INT) AS DECLARE @internal_param1 INT = @param1; SELECT * FROM Table WHERE Column = @internal_param1
  • @DavidG:虽然这可以解决参数嗅探,但与 OPTION (OPTIMIZE FOR ...)OPTION (RECOMPILE) 在个别麻烦的语句上相比,这是一种不直观的方法。不需要在本地复制参数。
  • @JeroenMostert 我不推荐其中任何一个作为参数嗅探的解决方案。

标签: c# sql-server entity-framework stored-procedures


【解决方案1】:

发现的问题是 SQL Server 中的参数嗅探。有多种方法可以处理此问题,但最适合您的场景的方法取决于您的实际用例、利用率等。

这里有一些选项。

  1. 每次执行都重新编译存储过程。这可能会导致 CPU 使用率非常高,并且通常是多余的。除非您有充分的理由,否则我不会推荐此选项。实施:使用 WITH RECOMPILE 或查询末尾的 OPTION(RECOMPILE) 提示。

  2. 优化提示。这可能是参数嗅探的一种解决方法,但可能会导致所有查询的执行计划低于标准。通常,这不是最佳方法。实施:使用 OPTION(OPTIMIZE FOR UNKNOWN)

  3. 将参数复制到局部变量。在旧版本的 SQL Server 中更为常见。实现:声明一个局部变量,然后将输入参数中的值复制到局部变量。 DECLARE @local_var1 char(1) = @InputParam1;

  4. 在查询级别关闭参数嗅探。此方法使用 QUERYTRACEON 提示。对于这种情况,这可能是最优化的方法。我建议将此选项作为主要策略进行探索。要实施:将 OPTION(QUERYTRACEON 4136) 添加到查询的末尾。

例子:

SELECT * FROM  dbo.MyTable T
WHERE T.Col1 = @Param1 and T.Col2 = @Param2
OPTION(QUERYTRACEON 4136)

【讨论】:

  • 我实现了 3 号,它几乎奏效了。通过在 SP 中添加一些调试信息,我能够确定存储过程没有得到提供给它的参数。我最终将整个调用转换为字符串并将其传递给 SqlQuery 函数。
【解决方案2】:

我最终不得不将整个调用转换为我传递给 SqlQuery 函数的单个字符串。

            string sql = string.Format("exec spGetDupWOs @ProjectName=N'{0}',@City=N'{1}',@State=N'{2}',@ProjectNumber=N'{3}',@County=N'{4}',@Owner=N'{5}',@QuoteRecipients=N'{6}',@ProjectID={7}", 
                project.ProjectName, 
                project.City,
                project.State,
                project.ProjectNumber,
                project.County,
                project.Owner,
                quoteRecipientsList, 
                "null");

是的,我必须在字符串中包含 N 前缀才能使其工作,我不知道为什么但它工作了。

感谢大家的帮助。没有你的帮助,我无法解决这个问题。

【讨论】:

    猜你喜欢
    • 2013-10-31
    • 1970-01-01
    • 1970-01-01
    • 2016-12-28
    • 2015-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多