为啥通过 ASP.NET 方法运行查询比原生 SQL 需要更长的时间?

Posted

技术标签:

【中文标题】为啥通过 ASP.NET 方法运行查询比原生 SQL 需要更长的时间?【英文标题】:Why does running a query through ASP.NET method take longer than native SQL?为什么通过 ASP.NET 方法运行查询比原生 SQL 需要更长的时间? 【发布时间】:2015-02-25 11:21:11 【问题描述】:

以下过程insert_attach 在dbms 上使用less than one second,但通过asp.net 方法运行时需要7 seconds ! :

 public static int InsertAttach(int taskCode, int transYear, int transSerial, int empNum)
    

        using (IfxConnection con = new IfxConnection(ConfigurationSettings.AppSettings["cmr"].ToString()))
        
            int res = 0;
            StringBuilder cmdTxt = new StringBuilder();
            cmdTxt.Append("insert_attach");

            using (var myIfxCmd = new IfxCommand(cmdTxt.ToString(), con))
            

                myIfxCmd.CommandType = CommandType.StoredProcedure;
                myIfxCmd.Parameters.Add("al_task_code", IfxType.Integer);
                myIfxCmd.Parameters.Add("al_trans_year", IfxType.Integer);
                myIfxCmd.Parameters.Add("al_trans_serial", IfxType.Integer);
                myIfxCmd.Parameters.Add("al_emp_num", IfxType.Integer);

                myIfxCmd.Parameters[0].Value = ((object)taskCode) ?? DBNull.Value;
                myIfxCmd.Parameters[1].Value = ((object)transYear) ?? DBNull.Value;
                myIfxCmd.Parameters[2].Value = ((object)transSerial) ?? DBNull.Value;
                myIfxCmd.Parameters[3].Value = ((object)empNum) ?? DBNull.Value;
                if (con.State == ConnectionState.Closed)
                
                    con.Open();//takes no time
                

                object obj = myIfxCmd.ExecuteScalar();//takes longer time !!
                if (obj != null && !string.IsNullOrEmpty(obj.ToString()))
                
                    res = int.Parse(obj.ToString());
                
            
            con.Close();
            con.Dispose();
            return res;
        

    

我的程序:

CREATE PROCEDURE insert_attach(
    al_task_code        INT,
    al_trans_year       INT,
    al_trans_serial     INT,
    al_emp_num          INT
)

returning INT;
define ll_state_serial , ll_prev_trans integer;
define ls_values_key VARCHAR(60);
define lbt_file_content , lbt_file_signed , lbt_file_null , lbt_file,  lbt_file_origin REFERENCES byte;

let lbt_file_null = NULL;


SELECT a.new_state_serial,
       values_key,
       a.prev_trans INTO     ll_state_serial,
       ls_values_key,
       ll_prev_trans
FROM   crm_trans             a
WHERE  a.task_code = al_task_code
       AND a.trans_year = al_trans_year
       AND a.trans_serial = al_trans_serial;


DELETE 
FROM   crm_tempdetails
WHERE  temp_serial IN (SELECT temp_serial
                       FROM   crm_tempsigned
                       WHERE  task_code = al_task_code
                              AND values_key = ls_values_key
                              AND emp_num = al_emp_num);

DELETE 
FROM   crm_tempsigned
WHERE  task_code = al_task_code
       AND values_key = ls_values_key
       AND emp_num = al_emp_num
       AND serial != 0;

-- insert into temp 
INSERT INTO crm_tempsigned
  (
    task_code,
    values_key,
    trans_year,
    trans_serial,
    serial,
    file_type,
    file_content,
    file_signed,
    emp_num,
    doc,
    selected
  )
SELECT a.task_code,
       ls_values_key,
       a.trans_year,
       a.trans_serial,
       a.serial,
       a.file_type,
       a.attach_content,
       lbt_file_null,
       al_emp_num,
       a.doc,
       1
FROM   crm_taskattachements     a,
       crm_trans                b
WHERE  a.task_code = b.task_code
       AND a.trans_serial = b.trans_serial
       AND a.trans_year = b.trans_year
       AND a.task_code = al_task_code
       AND b.trans_year = al_trans_year
       AND b.prev_trans = ll_prev_trans
       AND b.values_key = ls_values_key
       AND (
               a.serial IN (SELECT x.file_serial
                            FROM   crm_attachdetails x
                            WHERE  x.task_code = a.task_code
                                   AND x.trans_serial = a.trans_serial
                                   AND x.trans_year = a.trans_year
                                   AND x.emp_num = al_emp_num)
               OR 0 = (
                      SELECT COUNT(*)
                      FROM   crm_attachdetails x
                      WHERE  x.task_code = a.task_code
                             AND x.trans_serial = a.trans_serial
                             AND x.trans_year = a.trans_year
                  )
           );
RETURN 1;
END PROCEDURE

【问题讨论】:

您是否从您在 dbms 上测试过的同一台计算机上调用了 asp.net 方法?这里我的意思是确认没有网络开销。 @JenishRabadiya:是的,在同一台电脑上,通过同一网络,打开连接不需要时间,但是通过我的方法执行过程需要更长的时间!! 那个确切的场景通常是由于参数嗅探虽然我不熟悉 Informix 如何处理这些事情 @just_name 您使用的是什么版本的 Visual Studio? 您是仅在 asp.net 中运行该方法,还是它是更大进程的一部分?如果是更大进程的一部分,是否有其他代码暂时阻止您在此处插入?尝试在 7 秒内在 SSMS 中运行“sp_who2 active”,以查看是否有其他任何可能阻止您的 SPID。 【参考方案1】:

这个问题目前只能由你来回答。您需要分析您的应用程序,报告会准确告诉您哪个部分需要多长时间。

从那里开始,您可以开始调查为什么某件事需要很长时间,然后您可以提出一个具体的问题,我们或许可以帮助您解决。现在有一大段代码,你说的只是需要 7 秒。我们能做的就是玩猜谜游戏,不是吗?

说实话,我什至不知道你是怎么得到 7 秒的?你有没有在你的代码中加入一个计时器,如果有的话,你有没有看墙上的时钟等等?

请查看 Profiler。一个流行的解决方案是 DotTrace: https://www.jetbrains.com/profiler/

见:Profiling C# / .NET applications

【讨论】:

【参考方案2】:

我建议安装 Glimpse here 或从 Nuget 安装。虽然您已经收到的一些答案解释了一些可能发生的事情,但没有达到 7 秒的差异。 Glimpse 易于配置,应该可以快速为您指出问题的正确方向。

【讨论】:

【参考方案3】:

使用包装解决方案时总会有一些开销。在此示例中,您期望 C# 代码与本机 SQL 查询一样运行。鉴于 C# 在对数据库运行查询之前必须采取许多额外的步骤,因此需要更长的时间是有道理的。

C# 代码在尝试执行之前也会进行大量类型检查、强制转换等,所以也许您可能会查看您的代码,看看您是否可以在 C# 中进行任何优化?或者您可能需要研究使用 C# 执行大型查询的优化方法。

【讨论】:

您能建议对我的 C# 方法进行一些优化吗? 虽然我不同意你所说的任何事情,但这并不能解释 7 秒的延迟。【参考方案4】:

我认为一个好的开始将是在主要步骤中添加跟踪(等连接打开),然后与一个 Trace 并行运行 SQL Profiler。一旦你知道了问题区域,你就可以使用不同的策略来优化这些东西。请分享您的统计数据,然后我们可以讨论优化问题。

【讨论】:

【参考方案5】:

7 秒对于插入操作来说是很多时间。在您的代码中使用的 Informix 数据提供程序可能正在执行一组操作,这些操作包含在 IFXCommand.Execute 方法中。您可以使用 ILSpy 或 Reflector 工具来反汇编数据提供者并验证实际需要时间的额外代码是什么。

我同意 Nathan White 在 C# 中执行大量类型检查和强制转换,这可能会影响函数的性能。

【讨论】:

【参考方案6】:

您尝试使用 ARITHABORT 选项。 SET ARITHABORT 在您从应用程序登录 sql 后打开。

SqlCommand CmdArithabort = new SqlCommand("SET ARITHABORT ON", ConnApp);
CmdArithabort.ExecuteNonQuery();

在您的过程调用之前添加这一行。这可能会对您有所帮助

参考这个链接:https://msdn.microsoft.com/en-us/library/ms190306.aspx

见备注部分。第一行是

您应该始终在登录会话中将 ARITHABORT 设置为 ON。将 ARITHABORT 设置为 OFF 会对查询优化产生负面影响,从而导致性能问题。

【讨论】:

以上是关于为啥通过 ASP.NET 方法运行查询比原生 SQL 需要更长的时间?的主要内容,如果未能解决你的问题,请参考以下文章

为啥某些 .Net 程序集无法通过 AppDomain 的 GetAssemblies() 方法获得?

从 ASP.NET 启动进程 - 为啥进程会立即终止?

防止SQL注入

我的ASP.NET 程序点击按钮为啥没有反应?

ASP.Net中ListView 不能启用编辑的功能了 为啥?

为啥很多ASP.NET网页初次访问很慢,以后几次访问很快