C# 比较大型数据集时使用嵌套循环时速度较慢

C# 比较大型数据集时使用嵌套循环时速度较慢,c#,linq,entity-framework,C#,Linq,Entity Framework,我正在使用LINQtoEntities检索两个数据集列表。它们都在同一个数据库中,但我需要将一个表转换为任务表,因为它已集成到我的日历中。我敢肯定,这里不值得详细讨论,但我希望加快匹配id和创建新任务对象的过程。这是一个一劳永逸的代码片段,所以即使速度很慢,我也可以让程序在一夜之间运行。然而,为了将来的参考,我想要一些关于提高效率的建议 var accounts = data.Accounts.ToList().OrderBy(a => a.ID); Incidents[] Inciden

我正在使用LINQtoEntities检索两个数据集列表。它们都在同一个数据库中,但我需要将一个表转换为任务表,因为它已集成到我的日历中。我敢肯定,这里不值得详细讨论,但我希望加快匹配id和创建新任务对象的过程。这是一个一劳永逸的代码片段,所以即使速度很慢,我也可以让程序在一夜之间运行。然而,为了将来的参考,我想要一些关于提高效率的建议

var accounts = data.Accounts.ToList().OrderBy(a => a.ID);
Incidents[] Incidents = data.Incidents.ToArray();

        for (int i=0;i<Incidents.Length;i++)
        {
            foreach (var a in accounts)
            {
                if (a.Acct_CID == Incidents[i].CustomerID)
                {
                    Tasks t = new Tasks();
                    t.creator_id = a.ID;
                    t.start_date = Incidents[i].DateOpened;
                    t.end_date = Incidents[i].DateCLosed;
                    t.product_code = Incidents[i].ProductCode;
                    t.install_type = Incidents[i].InstallType;
                    t.os = Incidents[i].OSType;
                    t.details = Incidents[i].Description;
                    t.solution = Incidents[i].Solution;
                    t.creator_name = Incidents[i].TechID;
                    t.category = Incidents[i].Title;
                    t.text = "Ticket for" + " " + Incidents[i].Name;
                    if (t.end_date == DateTime.MinValue || t.end_date == null)
                        t.status_id = 6;
                    else t.status_id = 7;
                    data.Tasks.Add(t);
                    break;
                }
            }
        }
        data.SaveChanges();
var accounts=data.accounts.ToList().OrderBy(a=>a.ID);
事件[]事件=data.incents.ToArray();
对于(int i=0;i替换该行

var accounts = data.Accounts.ToList().OrderBy(a => a.ID);
用这个

var accounts = data.Accounts.OrderBy(a => a.ID).ToList();
这将让数据库进行排序,然后缓存排序的结果。您现在拥有的内容会将所有内容都拉入,然后在每次到达
foreach
循环时对它们进行排序(
accounts
再次被枚举)

我不能说它会有很大的改进,但是如果您的数据集足够大,多次重新排序一个大的列表肯定会减慢您的速度


第二眼,每次你不只是排序<代码>帐号,但是你似乎只寻找记录的一小部分,但是你正在对整个数组进行迭代。考虑替换< /P>

    foreach (var a in accounts)
        {
            if (a.Acct_CID == Incidents[i].CustomerID)
            {


为什么不连接表并动态创建任务

var tasks = from i in data.Incidents
            join a in data.Accounts on i.CustomerID equals a.Acct_CID
            select new Tasks()
            {
                creator_id = a.ID,
                start_date = i.DateOpened,
                end_date = i.DateCLosed
                // ...
            };
顺便说一句,我不认为排序在这里有意义,所以无论您以何种顺序将创建的任务添加到数据库中都不重要

// Query will not be executed until here
foreach(var task in tasks)
   data.Tasks.Add(task);
data.SaveChanges();

我将
加入
数据库中的结果

var joinedResult = data.Accounts.Join(data.Incidents, 
                                      a => a.Acct_CID, 
                                      i => i.CustomerID, 
                                      (a, i) => new { Account = a, Incident = i });

foreach (var item in joinedResult)
{
    Tasks t = new Tasks();
    t.creator_id = item.Account.ID;
    t.start_date = item.Incident.DateOpened;
    ........

}

创建帐户的查找

var accountsLookup = data.Accounts.ToLookup(a => a.Acct_CID);
foreach (var incident in data.Incidents)
{
    foreach (var a in accountsLookup[incident.CustomerID])
    {
        Tasks t = new Tasks();
        t.creator_id = a.ID;
        ...
    }
}
data.SaveChanges();

如果帐户是唯一的,您还可以创建一个字典

var accountsDict = data.Accounts.ToDictionary(a => a.Acct_CID);
foreach (var incident in data.Incidents)
{
    Account a;
    if (accountsDict.TryGetValue(incident.CustomerID, out a)
    {
        Tasks t = new Tasks();
        t.creator_id = a.ID;
        ...
    }
}
data.SaveChanges();

这将比第一个变体快。请注意,字典的查找时间是恒定的,不取决于它的大小。因此,循环的执行时间基本上是O(n)。原始实现的执行时间是O(n^2)执行时间。

抱歉,这是一个代码中没有显示的单独方法的排序,这是一个巨大的项目。我很惊讶我没有想到这一点。今天运行coffeeless,感谢您的帮助sirI,我很惊讶我也没有想到它。@Chazt3n欢迎:)顺便说一句,尽可能晚地调用ToList()、ToArray()和其他To*操作。所有这些都会强制执行查询,后续处理只会在内存中进行。不能在LINQ to Entities查询中构造实体或复杂类型“TRX.CRM.Dashboard.Web.UI.Models.Tasks”。@Chazt3n您可以返回匿名对象并映射到您的任务实体。如果可以,我会标记您的两个答案都是正确的,这确实是一个错误掷硬币,我给了你们一个+1,谢谢你们的帮助,你们真是太棒了。在这个例子中,它似乎没有迭代过任何东西?我只是无法看到这个迭代,还是它实际上不认为有一个列表?超时已过期。操作完成前已过超时时间,或者服务器没有响应。我得到了这个错误,我可以通过调用data.Accounts和data.incents上的tolist()来修复它,但它甚至比嵌套循环还要慢。@Chazt3n-它不仅仅是OrderBy。其中,选择,分组等。。。每次枚举IEnumerable时(例如每次启动新的
foreach
),返回IEnumerable的所有内容都将重新求值。在使用数据之前,最好始终将
ToList()
ToDictionary()
等作为最后一件事。这就是最终解决问题的原因,感谢您的帮助和时间,但是我必须开始一个新问题来解决问题。为什么您不接受我的答案,如果您使用我的查询并按照我所说的
操作,您可以返回匿名对象并将其映射到您的任务实体
?因为我很清楚我不知道如何使用Linq to entities实现这一点,我希望您在一瞬间感觉不好。您做得很好:)顺便说一句,完全改变您的问题并不好,因为其他人会阅读答案,他们不会理解发生了什么。最好开始新问题,或者在原始问题下写更新。因此,我将版本回滚到昨天的版本,并为您的程序提供了另一个技巧—查看一下它将执行从TasksDTO到幕后任务的映射。
    var tasks = (from i in data.Incidents
                     join a in data.Accounts on i.CustomerID equals a.Acct_CID
                     select new
                     {
                         creator_id = a.ID,
                         start_date = i.DateOpened,
                         end_date = i.DateCLosed,
                         product_code = i.ProductCode,
                         install_type = i.InstallType,
                         os = i.OSType,
                         details = i.Description,
                         solution = i.Solution,
                         creator_name = i.TechID,
                         category = i.Title,
                         text = "Ticket for" + " " + i.Name,
                         status_id = 7
                     }).AsEnumerable().Select(x => new
                         {
                             x.creator_id,
                             x.start_date,
                             x.end_date,
                             x.product_code,
                             x.os,
                             x.details,
                             x.solution,
                             x.creator_name,
                             x.category,
                             x.text,
                             x.install_type,
                             x.status_id
                         });


        foreach (var item in tasks)
        {
            Tasks t = new Tasks();
            t.os = item.os;
            t.id = item.creator_id;
            t.install_type = item.install_type;
            t.start_date = item.start_date;
            t.end_date = item.end_date;
            t.solution = item.solution;
            t.details = item.details;
            t.creator_name = item.creator_name;
            t.category = item.category;
            t.text = item.text;
            t.product_code = item.product_code;
             if (t.end_date == DateTime.MinValue || t.end_date == null)
                 t.status_id = 6;
             else t.status_id = 7;
             data.Tasks.Add(t);
        }
        data.SaveChanges();
    var tasks = (from i in data.Incidents
                     join a in data.Accounts on i.CustomerID equals a.Acct_CID
                     select new
                     {
                         creator_id = a.ID,
                         start_date = i.DateOpened,
                         end_date = i.DateCLosed,
                         product_code = i.ProductCode,
                         install_type = i.InstallType,
                         os = i.OSType,
                         details = i.Description,
                         solution = i.Solution,
                         creator_name = i.TechID,
                         category = i.Title,
                         text = "Ticket for" + " " + i.Name,
                         status_id = 7
                     }).AsEnumerable().Select(x => new
                         {
                             x.creator_id,
                             x.start_date,
                             x.end_date,
                             x.product_code,
                             x.os,
                             x.details,
                             x.solution,
                             x.creator_name,
                             x.category,
                             x.text,
                             x.install_type,
                             x.status_id
                         });


        foreach (var item in tasks)
        {
            Tasks t = new Tasks();
            t.os = item.os;
            t.id = item.creator_id;
            t.install_type = item.install_type;
            t.start_date = item.start_date;
            t.end_date = item.end_date;
            t.solution = item.solution;
            t.details = item.details;
            t.creator_name = item.creator_name;
            t.category = item.category;
            t.text = item.text;
            t.product_code = item.product_code;
             if (t.end_date == DateTime.MinValue || t.end_date == null)
                 t.status_id = 6;
             else t.status_id = 7;
             data.Tasks.Add(t);
        }
        data.SaveChanges();