c# - 查询数据库中的每条记录比使用 LINQ 更快

标签 c# mysql .net performance linq

我有大约 700K 行正在迭代。对于每一行,都会在数据库上运行 SELECT sql 语句,以检查当前记录中的“名称”字段是否存在于相应的表中。

数据库读取 700K 次让我觉得效率非常低,所以我选择在循环之前读取所有数据,将其存储在 DataTable 中,并通过 LINQ 检查相应的记录是否包含在 DataTable 中。迭代。

这样做后,性能明显下降。该过程现在需要大约两倍的时间才能完成(通过基准测试多次证明)。

这是原始(更快)代码:

for (int index = 0; index < dtSightings.Rows.Count; index++)
{
   DataTable dtResults = Utilities.ExecuteQueryMysqlString(connectionString, "SELECT name FROM my_table WHERE name = @name AND month_year = @monthYear", dictionary);

   if (dtResults == null || dtResults.Rows.Count == 0)
   {
   //Continue
   }
}

public static DataTable ExecuteQueryMysqlString(string connectionString, string sql, Dictionary<string, object> listParameters)
        {
            DataTable dtResults = new DataTable();

            if (string.IsNullOrWhiteSpace(connectionString) == false)
            {
                connectionString += ";Allow User Variables=True;";

                try
                {
                    using (MySqlConnection connection = new MySqlConnection(connectionString))
                    {
                        connection.Open();

                        using (MySqlCommand cmd = connection.CreateCommand())
                        {
                            cmd.CommandTimeout = 0;
                            cmd.CommandText = sql;

                            if (listParameters != null && listParameters.Count > 0)
                            {
                                foreach (string currentKey in listParameters.Keys)
                                {
                                    cmd.Parameters.Add(new MySqlParameter(currentKey, GetDictionaryValue(listParameters, currentKey)));
                                }
                            }

                            using (MySqlDataAdapter da = new MySqlDataAdapter(cmd))
                            {
                                da.Fill(dtResults);
                            }
                        }
                    }

                    return dtResults;
                }
                catch (Exception ex)
                {
                    MessageBox.Show("ERROR: " + ex.Message, "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return dtResults;
                }
            }
            else
            {
                return dtResults;
            }
        }

这是“优化”(但速度较慢)的代码:

DataTable dt= Utilities.ExecuteQueryMysqlString(connectionString, "SELECT name, month_year FROM my_table", null);

for (int index = 0; index < dtSightings.Rows.Count; index++)
{
  DataRow row = dt.AsEnumerable().Where(r => r.Field<string>("name").Equals(name, StringComparison.InvariantCultureIgnoreCase) && r.Field<DateTime>("month_year") == new DateTime(billYear, billMonth, 1)).FirstOrDefault();

  if (hasResidentBeenDiscoveredPreviously == null)
  {
    //Continue
  }
}

我不明白为什么第一种方法要快得多。是否有更优化的方法来代替第二种方法?

最佳答案

LINQ 方法很慢,因为 Where 基本上是线性搜索,并且在循环内执行时,确实会减慢进程。

您真正需要的是基于快速哈希的查找数据结构。我建议您使用带有这样的自定义数据的 HashSet (主要是为了支持不区分大小写的名称查找):

public struct NameDatePair : IEquatable<NameDatePair>
{
    public readonly string Name;
    public readonly DateTime Date;
    public NameDatePair(string name, DateTime date) { Name = name; Date = date; }
    static IEqualityComparer<string> NameComparer {  get { return StringComparer.InvariantCultureIgnoreCase; } }
    public override int GetHashCode() { return NameComparer.GetHashCode(Name) ^ Date.GetHashCode(); }
    public override bool Equals(object obj) { return obj is NameDatePair && Equals((NameDatePair)obj); }
    public bool Equals(NameDatePair other) { return NameComparer.Equals(Name, other.Name) && Date == other.Date; }
}

这是您在案例中使用它的方法(它应该比您的两种方法都要快得多):

var dt = Utilities.ExecuteQueryMysqlString(connectionString, "SELECT name, month_year FROM my_table", null);
var nameDatePairSet = new HashSet<NameDatePair>(dt.AsEnumerable().Select(
    r => new NameDatePair(r.Field<string>("name"), r.Field<DateTime>("month_year"))));

for (int index = 0; index < dtSightings.Rows.Count; index++)
{
    var dr = dtSightings.Rows[index];
    var name = dr.Field<string>("name");
    var billYear = dr.Field<int>("billYear");
    var billMonth = dr.Field<int>("billMonth");
    bool exists = nameDatePairSet.Contains(new NameDatePair(name, new DateTime(billYear, billMonth, 1)));
}

(由于您没有显示变量 namebillYearbillMonth 来自哪里,上面的代码有一些猜测,您可以根据您的需要进行调整)

关于c# - 查询数据库中的每条记录比使用 LINQ 更快,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40129011/

相关文章:

c# - 处理功能键按下

mysql - unknown.binary :mysql-connector-java-5. 1.23-bin :jar:SNAPSHOT is missing, 的 POM 没有可用的依赖信息

mysql - 如何通过非选定属性或任何替代方式组合选择不同和顺序?

mysql - 在 MySQL 中存储发票

c# - 如何使用 LINQ to Objects 安排作业而不重叠?

c# - 如何使用 NHibernate ICriteria 查询键值对列表?

c# - 如何从类中的函数返回异常

c# - 泛型方法的可重用非泛型方法

c# - 确保调用基类的静态构造函数的最佳方法是什么?

c# - 来自 MemberExpression 的反射类型