【问题标题】:Slow LINQ Performance on DataTable Where Clause?DataTable Where 子句上的 LINQ 性能缓慢?
【发布时间】:2015-04-05 18:46:28
【问题描述】:

我正在使用 MySqlDataAdapter 将 MySQL 中的表转储到 DataTable 对象中。数据库输入和输出运行良好,但我的应用程序代码似乎存在性能问题,我可以追踪到特定的 LINQ 语句。

目标很简单,在 DataTable 的内容中搜索与特定字符串匹配的列值,就像传统的WHERE column = 'text' SQL 子句一样。

简化代码:

foreach (String someValue in someList) {
    String searchCode = OutOfScopeFunction(someValue);
    var results = emoteTable.AsEnumerable()
        .Where(myRow => myRow.Field<String>("code") == searchCode)
        .Take(1);
    if (results.Any()) {
        results.First()["columnname"] = 10;
    }
}

这个简化的代码被执行了数千次,someList 中的每个条目执行一次。当我运行 Visual Studio Performance Profiler 时,我看到“results.Any()”行被突出显示为消耗了 93.5% 的执行时间。

我尝试了几种不同的方法来优化此代码,但没有一种方法在将emoteTable DataTable 作为数据的主要来源的同时提高了性能。我可以在 foreach 之外将 emoteTable 转换为 Dictionary&lt;String, DataRow&gt;,但是我必须保持 DataTable 和 Dictionary 同步,这虽然仍然可以提高性能,但感觉不对。

三个问题:

  1. 这是在 DataTable 中搜索值的正确方法吗(相当于传统的 SQL WHERE 子句)?如果没有,应该怎么做?
  2. 补充1,不管正确的方式,最快的(执行时间)是多少?
  3. 为什么results.Any() 行会消耗 90% 以上的资源?在这种情况下,var results 行应该消耗资源更有意义,毕竟它是执行实际搜索的行,对吧?

感谢您的宝贵时间。如果我找到答案,我也会在这里发布。

【问题讨论】:

  • 你为什么使用.Where().Take(1)然后results.Any()?为什么不var result = emoteTable.AsEnumerable().FirstOrDefault(myRow =&gt; myRow.Field&lt;String&gt;("code") == searchCode) ?然后你可以检查结果是否为空......

标签: c# performance linq datatable


【解决方案1】:

Any() 占用了 90% 的时间,因为 resultonly executed when you call Any()。在您调用Any() 之前,实际上并未进行查询。

问题似乎在于您首先将整个表提取到内存中,然后再进行搜索。您应该指示您的数据库进行搜索。
此外,当您调用results.First() 时,整个results 查询将再次执行。

考虑到延迟执行,您应该编写类似

var result = emoteTable.AsEnumerable()
    .Where(myRow => myRow.Field<String>("code") == searchCode)
    .FirstOrDefault();

if (result != null) {
    result["columnname"] = 10;
}

【讨论】:

  • 也许我误用了 DataTableDataAdapter 构造。我认为这些类的目的是允许更快的客户端与数据交互,然后将更改批量推送到数据库。我有问题的代码拉下一个表,将其与本地数据进行比较,当它发现差异时更新DataTable,完成后将更改推送到数据库(通过DataAdapter)。有没有更好的方法来做到这一点?
  • 我相信“更快”的部分来自这样一个事实,即这些是较低级别的结构,因此开销较小,尽管它可能非常小。我无法告诉您哪种方式更适合您的特定目的,您应该尝试一对看看(例如,带有SubmitChanges() 的 Linq2Sql 数据上下文,MySQL 中的存储过程,循环中的直接查询)。如果您保持当前的方法很好,只需确保您了解 Linq 延迟执行的概念。
【解决方案2】:

你已经实现的几乎是加入:

var searchCodes = someList.Select(OutOfScopeFunction);
var emotes = emoteTable.AsEnumerable();

var results = Enumerable.Join(emotes, searchCodes, e=>e, sc=>sc.Field<String>("code"), (e, sc)=>sc);

foreach(var result in results)
{
   result["columnname"] = 10;
}

Join 可能会使用某种查找优化对这两个列表的访问。

但我要做的第一件事是完全放弃结合 DataTable 和 LINQ 的想法。它们是两种不同的技术,很难断言它们在结合后可能在内部做什么。

您是否尝试过进行原始 UPDATE 调用?您希望更新多少项?

【讨论】:

  • 我最初进行了原始更新调用,性能还可以,除了我执行了大量的更新调用。我的印象是我可以通过 DataAdapter/DataTable 复制内存中的表并在 DataTable 中进行修改,然后将其同步回服务器。也许我的架构有缺陷。我愿意接受您拥有约 10k 拖​​车表的想法,您需要对其进行数千次修改(更新不同的行)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-20
  • 2019-12-20
  • 1970-01-01
  • 2015-04-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多