C# 后续执行中的值与原始值相差太大,计划将不是最优的

C# 后续执行中的值与原始值相差太大,计划将不是最优的,c#,sql-server,performance,C#,Sql Server,Performance,要纠正这种情况,可以在查询中指定选项(重新编译)。将查询添加到存储过程不会帮助您解决此特定问题,除非 通过重新编译创建过程 其他人已经提到了这一点(“参数嗅探”),但我认为对这一概念的简单解释不会有什么坏处。我遇到了一个与此问题症状的标题完全匹配的问题 在我的例子中,问题是结果集被应用程序的.NET代码打开,而它在每个返回的记录中循环,并对数据库执行另外三个查询!超过数千行,这误导了原始查询,使其看起来像是根据SQL Server的计时信息完成得很慢 因此,修复方法是重构进行调用的.NET代码,

要纠正这种情况,可以在查询中指定
选项(重新编译)
。将查询添加到存储过程不会帮助您解决此特定问题,除非 通过重新编译创建过程


其他人已经提到了这一点(“参数嗅探”),但我认为对这一概念的简单解释不会有什么坏处。

我遇到了一个与此问题症状的标题完全匹配的问题


在我的例子中,问题是结果集被应用程序的.NET代码打开,而它在每个返回的记录中循环,并对数据库执行另外三个查询!超过数千行,这误导了原始查询,使其看起来像是根据SQL Server的计时信息完成得很慢


因此,修复方法是重构进行调用的.NET代码,使其在处理每一行时不会打开结果集。

在测试环境中也存在同样的问题,尽管实时系统(在同一SQL server上)运行正常。添加选项(重新编译)和选项(优化(@p1未知))没有帮助

我使用SQL Profiler捕获.net客户端发送的确切查询,发现该查询用
exec sp_executesql N'select…
包装,并且参数已声明为nvarchars-正在比较的列是简单的varchars

将捕获的查询文本放入SSMS后,确认其运行速度与从.net客户端运行时一样慢

我发现将参数类型更改为AnsiText可以解决问题:

p=cm.CreateParameter()
p、 ParameterName=“@公司”
p、 价值=公司
p、 DbType=DbType.AnsiString
cm.参数。添加(p)


我永远无法解释为什么测试环境和实时环境在性能上有如此显著的差异。

我意识到OP没有提到存储过程的使用,但在使用存储过程时,有一种替代解决方案可以解决参数嗅探问题,它不太优雅,但在
选项(重新编译)时对我有效
似乎什么都不做

只需将您的参数复制到过程中声明的变量,然后使用它们即可

例如:

ALTER PROCEDURE [ExampleProcedure]
@StartDate DATETIME,
@EndDate DATETIME
AS
BEGIN

--reassign to local variables to avoid parameter sniffing issues
DECLARE @MyStartDate datetime,
        @MyEndDate datetime

SELECT 
    @MyStartDate = @StartDate,
    @MyEndDate = @EndDate

--Rest of procedure goes here but refer to @MyStartDate and @MyEndDate
END

在我的例子中,问题是我的实体框架正在生成使用
exec sp\u executesql
的查询

当参数在类型上不完全匹配时,执行计划不会使用索引,因为它决定将转换放入查询本身。 正如您可以想象的那样,这会导致更慢的性能

在我的例子中,列被定义为CHR(3),实体框架在查询中传递N'str',这导致从nchar到char的转换。因此,对于如下所示的查询:

ctx.Events.Where(e=>e.Status==“Snt”)

它正在生成一个SQL查询,如下所示:

来自[ExtEvents]作为[extt1]。。。
其中(N“Snt”=[Extent1].[Status])…


在我的例子中,最简单的解决方案是更改列类型,或者,您也可以首先努力使代码通过正确的类型。

希望您的具体问题现在已经解决,因为这是一篇旧文章

以下
SET
选项可能会影响计划结果(末尾的完整列表)

以下两项声明来自

将ARITHABORT设置为OFF会对查询优化产生负面影响,从而导致性能问题

SQL Server Management Studio的默认arithaport设置为启用。将ARITHABORT设置为OFF的客户端应用程序可能会收到不同的查询计划,这使得对性能差的查询进行故障排除变得困难。也就是说,同一查询在ManagementStudio中执行得很快,但在应用程序中执行得很慢

另一个需要理解的有趣主题是
参数嗅探
,如中所述

还有一种可能性是在使用Unicode输入参数时(内部)将VARCHAR列转换为NVARCHAR,如中所述

针对未知优化

在SQL Server 2008和以上中,考虑未知的优化。未知:指定查询优化器在查询优化期间使用统计数据而不是初始值来确定局部变量的值

选项(重新编译)

如果重新编译是唯一的解决方案,请使用“选项(重新编译)”而不是“使用重新编译”。它有助于参数嵌入优化。阅读

设置选项

以下
SET
选项可能会影响计划重用,具体取决于

  • ANSI_NULL_DFLT_关闭2。3上的ANSI_NULL_DFLT_。ANSI_空值4。美国国家标准协会5。ANSI_警告6。阿里沙波特7。CONCAT_NULL_产生8个。第一天9点。日期格式10。执行计划11。语言12。不可浏览13。数字_ROUNDABORT 14。带引号的标识符

  • 我今天遇到了这个问题,这解决了我的问题:

    我把我的SP的开始放在这个:设置ARITHABORT


    这帮你忙

    我刚刚遇到了这个问题。针对在SSMS中返回次秒响应的视图运行的选择。但是运行sp_executesql需要5到20秒。为什么?因为当我通过sp_executesql运行时查看查询计划时,它没有使用正确的索引。它还进行索引扫描,而不是搜索。对我来说,解决方案只是创建一个简单的sp,用传递的参数执行查询。当通过sp_executesql运行时,它使用了正确的索引,并且没有进行扫描。如果您想进一步改进它,请确保在有sp时使用command.CommandType=CommandType.StoredProcedure,然后它不使用sp_executesql,它只使用EXEC,但这只减少了ms的结果

    这段代码在一个有数百万条记录的数据库上运行了不到一秒

       public DataTable FindSeriesFiles(string StudyUID)
        {
            DataTable dt = new DataTable();
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                connection.Open();
                using (var command = new SqlCommand("VNA.CFIND_SERIES", connection))
                {
                    command.CommandType = CommandType.StoredProcedure;
                    command.Parameters.AddWithValue("@StudyUID", StudyUID);
                   using (SqlDataReader reader = command.ExecuteReader())
                    {
                        dt.Load(reader);
                    }
                    return dt;
                }
            }
        }
    
    using (SqlConnection conn = new SqlConnection("<CONNECTION_STRING>")) {
        conn.Open();
    
        using (SqlCommand comm = new SqlCommand("SET ARITHABORT ON", conn)) {
            comm.ExecuteNonQuery();
        }
    
        // Do your own stuff here but you must use the same connection object
        // The SET command applies to the connection. Any other connections will not
        // be affected, nor will any new connections opened. If you want this applied
        // to every connection, you must do it every time one is opened.
    }
    
        object value = cmd.ExecuteScalar();
    
        if (value == null)
            return 0;
        else
            return (double)value;
    
    cmd.Parameters.Add("@TrustAccountID1", SqlDbType.Int).Value = trustAccountId;
    cmd.Parameters.Add("@UserID1", SqlDbType.Int).Value = userId;
    cmd.Parameters.Add("@TrustAccountID2", SqlDbType.Int).Value = trustAccountId;
    cmd.Parameters.Add("@UserID2", SqlDbType.Int).Value = userId;
    
    cmd.Parameters.Add("@TrustAccountID", SqlDbType.Int).Value = trustAccountId;
    cmd.Parameters.Add("@UserID", SqlDbType.Int).Value = userId;
    
    select TrustAccountValue from
    (
     SELECT MAX (tal.trustaccountlogid), tal.TrustAccountValue
     FROM  TrustAccountLog AS tal
     INNER JOIN TrustAccount ta ON ta.TrustAccountID = tal.TrustAccountID
     INNER JOIN Users usr ON usr.UserID = ta.UserID
     WHERE usr.UserID = 70402 AND
     ta.TrustAccountID = 117249 AND
     tal.TrustAccountLogDate < '3/1/2010 12:00:00 AM'
     group by tal.TrustAccountValue
    ) q
    
    set language us_english
    go
    select @@language --us_english
    select convert(datetime, '3/1/2010 12:00:00 AM')
    go
    set language british
    go
    select @@language --british
    select convert(datetime, '3/1/2010 12:00:00 AM')
    
    select convert(datetime, '20100301 00:00:00') --midnight 00, noon 12
    
    tal.TrustAccountLogDate < @TrustAccountLogDate2
    
    ALTER PROCEDURE [ExampleProcedure]
    @StartDate DATETIME,
    @EndDate DATETIME
    AS
    BEGIN
    
    --reassign to local variables to avoid parameter sniffing issues
    DECLARE @MyStartDate datetime,
            @MyEndDate datetime
    
    SELECT 
        @MyStartDate = @StartDate,
        @MyEndDate = @EndDate
    
    --Rest of procedure goes here but refer to @MyStartDate and @MyEndDate
    END
    
    SET QUOTED_IDENTIFIER ON
    GO
    SET ANSI_NULLS ON
    GO
    SET ARITHABORT ON
    GO
    
       public DataTable FindSeriesFiles(string StudyUID)
        {
            DataTable dt = new DataTable();
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                connection.Open();
                using (var command = new SqlCommand("VNA.CFIND_SERIES", connection))
                {
                    command.CommandType = CommandType.StoredProcedure;
                    command.Parameters.AddWithValue("@StudyUID", StudyUID);
                   using (SqlDataReader reader = command.ExecuteReader())
                    {
                        dt.Load(reader);
                    }
                    return dt;
                }
            }
        }
    
    CREATE PROCEDURE [VNA].[CFIND_SERIES]
        @StudyUID NVARCHAR(MAX)
    AS BEGIN
        SET NOCOUNT ON
        SELECT * 
        FROM CFIND_SERIES_VIEW WITH (NOLOCK) 
        WHERE [StudyInstanceUID] = @StudyUID
        ORDER BY SeriesNumber
    END
    
    public DataTable FindSeriesFiles(string StudyUID)
        {
            DataTable dt = new DataTable();
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                connection.Open();
                using (var command = connection.CreateCommand())
                {
                    command.CommandText =" SELECT * FROM CFIND_SERIES_VIEW WITH (NOLOCK) WHERE StudyUID=@StudyUID ORDER BY SeriesNumber";
                    command.Parameters.AddWithValue("@StudyUID", StudyUID);
                    using (SqlDataReader reader = command.ExecuteReader())
                    {
                        dt.Load(reader);
                    }
                    return dt;
                }
            }
        }