我有大约 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)));
}
(由于您没有显示变量 name
、billYear
和 billMonth
来自哪里,上面的代码有一些猜测,您可以根据您的需要进行调整)
关于c# - 查询数据库中的每条记录比使用 LINQ 更快,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40129011/