【问题标题】:SQLDataReader going too slow depending on criteriaSQLDataReader 速度太慢,具体取决于条件
【发布时间】:2015-04-10 13:47:13
【问题描述】:

我正在使用 SQLDataReader 在 asp.net 页面上填充 GridView (GridView1)。 SQLDataReader 在 C# Codebehind 中设置自己,如下所示:

        string MySQLString;
        MySQLString = "SELECT * FROM [vw_Report_Latest_v3_1] WHERE [FKID_Contract]=@Contract";
        if ((string)Session["TSAreaString"] != "") { MySQLString = MySQLString + " AND [L1_Name]=@PA1"; }
        if ((string)Session["TSSiteString"] != "") { MySQLString = MySQLString + " AND [L2_Name]=@PA2"; }
        if ((string)Session["TSFeatureString"] != "") { MySQLString = MySQLString + " AND [L3_Name]=@PA3"; }
        if ((string)Session["TSS1"] != "") { MySQLString = MySQLString + " AND [Spare1]=@S1"; }
        if ((string)Session["TSS2"] != "") { MySQLString = MySQLString + " AND [Spare2]=@S2"; }
        if ((string)Session["TSS3"] != "") { MySQLString = MySQLString + " AND [Spare3]=@S3"; }
        if ((string)Session["TSS4"] != "") { MySQLString = MySQLString + " AND [Spare4]=@S4"; }
        if ((string)Session["TSS5"] != "") { MySQLString = MySQLString + " AND [Spare5]=@S5"; }
        if ((string)Session["TSTaskString"] != "") { MySQLString = MySQLString + " AND [Operation_Name]=@PA4"; }
        if ((string)Session["TSTeamString"] != "") { MySQLString = MySQLString + " AND [Team_Name]=@Team"; }
        //finish
        MySQLString = MySQLString + " ORDER BY [OperationOrder], [L1_Name], [L2_Name], [L3_Name], [Operation_Name], [Team_Name]";
        try
        {
            Conn.Open();
            SqlCommand Cmd = new SqlCommand(MySQLString, Conn);
            Cmd.Parameters.AddWithValue("@Contract", Convert.ToInt32(invCID.Text));
            if ((string)Session["TSAreaString"] != "") { Cmd.Parameters.AddWithValue("@PA1", (string)Session["TSAreaString"]); }
            if ((string)Session["TSSiteString"] != "") { Cmd.Parameters.AddWithValue("@PA2", (string)Session["TSSiteString"]); }
            if ((string)Session["TSFeatureString"] != "") { Cmd.Parameters.AddWithValue("@PA3", (string)Session["TSFeatureString"]); }
            if ((string)Session["TSS1"] != "") { Cmd.Parameters.AddWithValue("@S1", (string)Session["TSS1"]); }
            if ((string)Session["TSS2"] != "") { Cmd.Parameters.AddWithValue("@S2", (string)Session["TSS2"]); }
            if ((string)Session["TSS3"] != "") { Cmd.Parameters.AddWithValue("@S3", (string)Session["TSS3"]); }
            if ((string)Session["TSS4"] != "") { Cmd.Parameters.AddWithValue("@S4", (string)Session["TSS4"]); }
            if ((string)Session["TSS5"] != "") { Cmd.Parameters.AddWithValue("@S5", (string)Session["TSS5"]); }
            if ((string)Session["TSTaskString"] != "") { Cmd.Parameters.AddWithValue("@PA4", (string)Session["TSTaskString"]); }
            if ((string)Session["TSTeamString"] != "") { Cmd.Parameters.AddWithValue("@Team", (string)Session["TSTeamString"]); }
            Cmd.Connection = Conn;
            SqlDataReader reader = Cmd.ExecuteReader(CommandBehavior.CloseConnection);
            GridView1.DataSource = reader;
            GridView1.DataBind();
        }
        finally
        {
            if (Conn != null) { Conn.Close(); }
        }

这给我带来了严重的问题。例如,如果我们将 L1_Name(通过给 TSAreaString 一个值)设置为“Town”,它将显示 L1_Name 为“Town”的所有内容。这很好,花花公子。需要几秒钟,因为它是一个大城镇。

但是,如果我们将 L1_Name 设置为“Town”并且将 TSS3(在本例中)设置为“County”,那么尽管检索的记录数量相同,或者有时更少,但它需要更长的时间 - 有时超过一分钟.

不幸的是,我们必须加入这个 - 我们不能只搜索“Town”,由于我们的客户强制要求,我们必须搜索“Town”和“County”。

从 - vw_Report_Latest_v3_1 运行的视图 - 运行得非常好。即使使用上述标准,也没有问题。两种方案 - Town 和 Town AND County,通过 SQL Server 2008 单独在视图上花费相同的时间。

我很确定这是某种阅读/绑定。

【问题讨论】:

  • 你的代码读起来让我头疼。您应该使用using 声明而不是try/finally。您还应该将所有相关参数存储在一个对象中,并将 that 存储在 Session 中,而不是在 Session 中存储为大量单独的对象。最后,你不应该在后面的代码中做数据库工作,你应该有一个单独的层来负责。
  • 如果是数据绑定问题,您可以测量执行查询的时间,以及执行数据绑定的时间……但这绝对不是数据绑定问题……一旦填充数据获取数据的查询是什么并不重要,只要它返回相同数量的数据。
  • 运行SQL管理工作室生成的查询,查看执行计划。
  • 必须承认我是这种 SQL 的新手。但是,我不认为 SQL 是答案 - 视图通过 SQL Management Studio 运行良好,即使有尽可能多的标准,并且通过上述代码隐藏运行正常。只是出于某种原因在 Codebehind 版本中添加了一个标准,这会减慢它的速度。
  • 这可能是AddWithValue() 的问题。有时 ADO.Net 会以破坏索引使用的方式猜测错误的参数类型。

标签: c# sql asp.net data-binding sqldatareader


【解决方案1】:

您的 SQL 连接代码看起来不是问题所在,也不是 GridView。尝试 [在更高的范围内] 获取您的 Session 变量并将其保存到 Dictionary<string, object>Dictionary<string, string> (或类似方法)并从中检索您的值,而不是针对每个值(两次)点击浏览器的会话。

您的视图/基础表的索引或索引都不存在或很麻烦。确保也检查这些。

需要考虑的一些事项: 1. 对于你想要做的事情,创建一个不需要你自己构建 SQL 查询的存储过程。当你将来增加复杂性时,你会让自己头疼。例如,而不是:

if ((string)Session["TSAreaString"] != "") { MySQLString = MySQLString + " AND [L1_Name]=@PA1"; }

...您的存储过程可以很容易地使用 COALESCE 或 ISNULL 来获得相同的结果。

--parameter for stored procedure
@TSAreaString nvarchar(max) = NULL


SELECT v.* 
FROM View v
WHERE v.TSAreaString = COALESCE(@TSAreaString, v.TSAreaString)

(SQL Server 语法)

如果您使用这种方法,您可以删除代码的上半部分,并在后半部分执行类似的操作:

Cmd.Parameters.AddWithValue("@Team", String.IsNullOrWhiteSpace((string)Session["TSTeamString"]) ? DBNull.Value : (string)Session["TSTeamString"]; 

但是,如果您要继续使用相同的方法:

  1. 使用 StringBuilder 代替字符串。它是不可变的,如果您担心性能,它会因此表现得更好。

  2. 将您的连接对象和命令对象包装到 using 子句中,以便暂时自动处理(长期而言,创建自己的类来处理不同的数据库操作)。

  3. 实际上这可能是您的问题的一部分,但我不确定,因为我以前没有按照您的方式这样做过。 不要让reader 对象成为网格视图的数据源,而是创建一个代表您的数据的类,并使用阅读器使用

    填充它的List<YourClass>

    while (reader.Read()) { YourList.Add(RetrieveYourClass(reader)); }

(^ SO 不是出于某种原因突出显示的代码)

【讨论】:

  • 我绝对知道我的代码不是最好的,但我现在倾向于索引(以及我缺乏索引)。我想我需要知道如何有效地索引表。
【解决方案2】:

您可以在几个方面进行改进。

  • 您应该使用 StringBuilder 而不是 string = string +"";效率更高
  • 我建议使用 !string.IsNullOrEmpty((string)Session[""]),而不是 != ""。这样它会捕获 null、string.empty 和 ""
  • (个人喜好)不要害怕空白。将 if 语句放在同一行.. 很难阅读。
  • 干得好,将东西包装在 {} 中,如果您不这样做,下一个人将是一场噩梦
  • 分离层是个好主意(正如@mason 提到的)。我个人有一个数据层来容纳查询。没有逻辑。然后是业务层或类层。它包含逻辑。清理、验证等……然后是后面的代码将值传递到类层。
  • 我看到了两种类型的业务层。有真正的对象风格。数据层转换数据表,然后将其加载到类中。查找 POCO 以了解这一点。另一个是我几年前开始的,是层系统。每个方法都是自包含的,只是将内容传递给数据层,如果是选择,则返回一个数据表。
  • 提取出与数据库联系的代码。 (见我的代码在底部)

你提到了一个观点。检查您的索引。因为这是 mysql...我不知道如何做到这一点,但是在使用 Microsoft SQL 时,您可以使用所谓的估计执行路径。因此您可以将 select 语句放入 MSSMS 软件中,而不是执行,您可以单击“显示估计执行路径”按钮,该按钮将为索引等提供建议。

这是您的数据层的外观以及它如何使用连接器(这是下一个代码块)

private MySQLConnector _MySQL = null;
protected MySQLConnector MySQL
{
   get
   {
      if (_MySQL == null)
      {
         _MySQL = new MySQLConnector();
      }
      return _MySQL;
   }
}

public void Update(int programId, int LocationId, string Name, string modifiedBy)
   {
   List<MySqlParameter> parameterList = new List<MySqlParameter>();

   parameterList.Add(new MySqlParameter("ProgramID", programId));
   parameterList.Add(new MySqlParameter("LocationId", LocationId));
   parameterList.Add(new MySqlParameter("Name", Name));
   if (!string.IsNullOrEmpty(modifiedBy))
   {
      parameterList.Add(new MySqlParameter("ModifiedBy", modifiedBy));
   }
   else
   {
      parameterList.Add(new MySqlParameter("ModifiedBy", DBNull.Value));
   }

   const string TheSql = @"
            UPDATE ProgramLocation
            SET
           Name = @Name,
               ModifiedOn = GETDATE(),
               ModifiedBy = @ModifiedBy
            WHERE
        ProgramID = @ProgramID
        AND LocationId = @LocationId";

   MySQL.ExecuteNonQuerySql(TheSql, parameterList);
}

这是联系数据库的代码。它有点过时了,您可能需要更改它用于联系 MySQL 数据库的包。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Data;
using System.Configuration;
using System.Reflection;
using MySql.Data.MySqlClient;

namespace DEFINETHENameSpace
{
    public class MySQLConnector
    {
        private string connString = null;

        public string TheConnectionString
        {
            get
            {
                if (string.IsNullOrEmpty(connString))
                {
                    //  connString = ConfigurationManager.ConnectionStrings["MySQLConnection"].ConnectionString; 
                    throw new Exception("No Connection String Specified");
                }

                return connString;
            }

            set
            {
                connString = value;
            }
        }

        private Exception errorMessage;

        public Exception ErrorMessage
        {
            get
            {
                return errorMessage;
            }

            set
            {
                errorMessage = value;
            }
        }

        #region ExecuteNonQuery
        /// <summary>
        /// THis will execute a non query, such as an insert statement
        /// </summary>
        /// <returns>1 for success, 0 for failed.</returns>
        /// <author>James 'Gates' R.</author>
        /// <createdate>8/20/2012</createdate>
        public int ExecuteNonQuery(string theSQLStatement)
        {
            int returnValue = 0;

            if (!string.IsNullOrEmpty(theSQLStatement))
            {
                MySqlConnection connection = new MySqlConnection(TheConnectionString);
                MySqlCommand command = connection.CreateCommand();

                try
                {
                    command.CommandText = theSQLStatement;
                    connection.Open();
                    command.ExecuteNonQuery();

                    //Success
                    returnValue = 1;
                }
                catch (Exception ex)
                {
                    returnValue = 0;
                    throw ex; //ErrorMessage = ex; 
                    // WriteToLog.Execute(ex.Message, EventLogEntryType.Error);
                }
                finally
                {
                    command.Dispose();
                    if (connection.State == System.Data.ConnectionState.Open)
                    {
                        connection.Close();
                    }

                    connection.Dispose();
                }
            }

            return returnValue;
        }

        /// <summary>
        /// THis will execute a non query, such as an insert statement
        /// </summary>
        /// <returns>1 for success, 0 for failed.</returns>
        /// <author>James 'Gates' R.</author>
        /// <createdate>8/20/2012</createdate>
        public int ExecuteNonQuery(string theSQLStatement, List<MySqlParameter> parameters)
        {
            if ((parameters != null) && (parameters.Count > 0))
            {
                return ExecuteNonQuery(theSQLStatement, parameters.ToArray());
            }
            else
            {
                return ExecuteNonQuery(theSQLStatement);
            }
        }

        /// <summary>
        /// THis will execute a non query, such as an insert statement
        /// </summary>
        /// <returns>1 for success, 0 for failed.</returns>
        /// <author>James 'Gates' R.</author>
        /// <createdate>8/20/2012</createdate>
        public int ExecuteNonQuery(string theSQLStatement, MySqlParameter[] parameters)
        {
            if ((parameters == null) || (parameters.Count() <= 0))
            {
                return ExecuteNonQuery(theSQLStatement);
            }

            int returnValue = 0;

            if (!string.IsNullOrEmpty(theSQLStatement))
            {
                MySqlConnection connection = new MySqlConnection(TheConnectionString);
                MySqlCommand command = connection.CreateCommand();

                try
                {
                    command.CommandText = theSQLStatement;
                    command.Parameters.AddRange(parameters);
                    connection.Open();
                    command.ExecuteNonQuery();

                    //Success
                    returnValue = 1;
                }
                catch (Exception ex)
                {
                    returnValue = 0;
                    throw ex; //ErrorMessage = ex; 
                    //WriteToLog.Execute(ex.Message, EventLogEntryType.Error);
                }
                finally
                {
                    command.Dispose();
                    if (connection.State == System.Data.ConnectionState.Open)
                    {
                        connection.Close();
                    }

                    connection.Dispose();
                }
            }

            return returnValue;
        }

        #endregion

        #region Execute
        /// <summary>
        /// THis will execute a query, such as an select statement
        /// </summary>
        /// <returns>Populated Datatable based on the sql select command.</returns>
        /// <author>James 'Gates' R.</author>
        /// <createdate>8/20/2012</createdate>
        public DataTable Execute(string theSQLStatement)
        {
            DataTable resultingDataTable = new DataTable();

            if (!string.IsNullOrEmpty(theSQLStatement))
            {
                MySqlConnection connection = new MySqlConnection(TheConnectionString);
                MySqlCommand command = connection.CreateCommand();

                try
                {
                    command.CommandText = theSQLStatement;
                    connection.Open();

                    MySqlDataAdapter dataAdapter = new MySqlDataAdapter(command.CommandText, connection);
                    dataAdapter.Fill(resultingDataTable);

                    //Success
                }
                catch (Exception ex)
                {
                    throw ex; //ErrorMessage = ex; 

                    //WriteToLog.Execute(ex.Message, EventLogEntryType.Error);
                }
                finally
                {
                    command.Dispose();
                    if (connection.State == System.Data.ConnectionState.Open)
                    {
                        connection.Close();
                    }

                    connection.Dispose();
                }
            }

            return resultingDataTable;
        }

        /// <summary>
        /// THis will execute a query, such as an select statement
        /// </summary>
        /// <returns>Populated Datatable based on the sql select command.</returns>
        /// <author>James 'Gates' R.</author>
        /// <createdate>8/20/2012</createdate>
        public DataTable Execute(string theSQLStatement, List<MySqlParameter> parameters)
        {

            if ((parameters != null) && (parameters.Count > 0))
            {
                return Execute(theSQLStatement, parameters.ToArray());
            }
            else
            {
                return Execute(theSQLStatement);
            }
        }

        /// <summary>
        /// THis will execute a query, such as an select statement
        /// </summary>
        /// <returns>Populated Datatable based on the sql select command.</returns>
        /// <author>James 'Gates' R.</author>
        /// <createdate>8/20/2012</createdate>
        public DataTable Execute(string theSQLStatement, MySqlParameter[] parameters)
        {
            if ((parameters == null) || (parameters.Count() <= 0))
            {
                return Execute(theSQLStatement);
            }

            DataTable resultingDataTable = new DataTable();

            if (!string.IsNullOrEmpty(theSQLStatement))
            {
                MySqlConnection connection = new MySqlConnection(TheConnectionString);
                MySqlCommand command = connection.CreateCommand();

                try
                {
                    command.CommandText = theSQLStatement;
                    connection.Open();

                    MySqlDataAdapter dataAdapter = new MySqlDataAdapter(command.CommandText, connection);
                    dataAdapter.SelectCommand.Parameters.AddRange(parameters);
                    dataAdapter.Fill(resultingDataTable);

                    //Success
                }
                catch (Exception ex)
                {
                    throw ex; //ErrorMessage = ex; 
                    //WriteToLog.Execute(ex.Message, EventLogEntryType.Error);
                }
                finally
                {
                    command.Dispose();
                    if (connection.State == System.Data.ConnectionState.Open)
                    {
                        connection.Close();
                    }

                    connection.Dispose();
                }
            }

            return resultingDataTable;
        }
    }
        #endregion
}

【讨论】:

  • StringBuilder 在这种情况下不会产生可衡量的差异。
  • 乔尔是正确的,it's been tested many times。并且为了防止无效的演员表(以防万一)我推荐!string.IsNullOrEmpty(Session[""] as string)
  • 好点。 “作为字符串”是比(字符串)更好的方法。 ^_^
【解决方案3】:

想通了,对于任何可能遇到类似情况的人。

基本上,正如上面其他人所说,一部分是 - 不要过多地提及会话状态。相反,我做了我以前做过的事情,因为无论如何我都需要页面上的标签中的会话,所以只需从页面上的标签中读取,而不是一直往返于会话中。

但是,主要部分是更改 SQL - 但只是一点点。而不是使用,说:

MySQLString = MySQLString + " AND [Spare1]=@S1";

我用过:

MySQLString = MySQLString + " AND ([Spare1] LIKE @S1)";

似乎封装了每个条件并使用 LIKE 是关键 - 现在它在所有情况下都运行得非常快。

【讨论】:

    猜你喜欢
    • 2018-03-14
    • 2017-02-28
    • 2023-01-19
    • 2015-06-06
    • 2011-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多