【问题标题】:Optimize code: Linq and foreach loop 15k records优化代码:Linq 和 foreach 循环 15k 记录
【发布时间】:2010-09-06 22:08:22
【问题描述】:

这是我的代码

void fixInstellingenTabel(object source, ElapsedEventArgs e)
{
    NASDataContext _db = new NASDataContext();

    List<Instellingen> newOnes = new List<Instellingen>();

    List<InstellingGegeven> li = _db.InstellingGegevens.ToList();
    foreach (InstellingGegeven i in li) {
        if (_db.Instellingens.Count(q => q.INST_LOC_REF == i.INST_LOC_REF && q.INST_LOCNR == i.INST_LOCNR && q.INST_REF == i.INST_REF && q.INST_TYPE == i.INST_TYPE) <= 0) {
            // There is no item yet. Create one.
            Instellingen newInst = new Instellingen();
            newInst.INST_LOC_REF = i.INST_LOC_REF;
            newInst.INST_LOCNR = i.INST_LOCNR;
            newInst.INST_REF = i.INST_REF;
            newInst.INST_TYPE = i.INST_TYPE;
            newInst.Opt_KalStandaard = false;
            newOnes.Add(newInst);
        }
    }
    _db.Instellingens.InsertAllOnSubmit(newOnes);
    _db.SubmitChanges();
}

基本上,InstellingGegevens 表由另一台服务器的某个过程填充。 然后我需要做的是检查该表中是否有新记录,并在 Instellingens 中填写新记录。

此代码在 15k 条记录上运行大约 4 分钟。我该如何优化它?还是存储过程的唯一方式?

此代码在计时器中运行,每 6 小时运行一次。如果存储过程是最好的,我如何在计时器中使用它?

        Timer Tim = new Timer(21600000); //6u
        Tim.Elapsed += new ElapsedEventHandler(fixInstellingenTabel);
        Tim.Start();

【问题讨论】:

    标签: c# asp.net linq-to-sql optimization timer


    【解决方案1】:

    在存储过程中执行此操作会快得多。我们做了非常相似的事情,表中只有大约 10 万个项目,每五分钟更新一次,并且包含更多字段。我们的作业运行大约需要两分钟,然后它会在三个数据库中的多个表中进行更新,因此您的作业合理地只需要几秒钟。

    您需要的查询类似于:

    create procedure UpdateInstellingens as
    
    insert into Instellingens (
      INST_LOC_REF, INST_LOCNR, INST_REF, INST_TYPE, Opt_KalStandaard
    )
    select q.INST_LOC_REF, q.INST_LOCNR, q.INST_REF, q.INST_TYPE, cast(0 as bit)
    from InstellingGeven q
    left join Instellingens i
      on q.INST_LOC_REF = i.INST_LOC_REF and q.INST_LOCNR = i.INST_LOCNR
      and q.INST_REF = i.INST_REF and q.INST_TYPE = i.INST_TYPE
    where i.INST_LOC_REF is null
    

    您可以从 SQL 服务器中的作业运行该过程,而完全不涉及任何应用程序,或者您可以使用 ADO.NET 从您的计时器执行该过程。

    【讨论】:

    • 这段代码很棒。它在不到一秒的时间内运行了 15k 条记录。我想了半秒钟。惊人的速度:) 你能解释一下这个查询是如何工作的吗?因为我不太明白。是这样的吗?:插入表格,从instellinGegevens中选择4项,其中inst_loc_ref为null。我不明白左连接的作用。为什么当你加入这 2 个表时,其中 1 个表没有对应的值时,它不会给出错误。
    • 编辑:好的,我得到了左连接。绝妙的想法:)谢谢! w3schools.com/sql/sql_join_left.asp
    【解决方案2】:

    一种优化方法是将Count(...) &lt;= 0 更改为Any()。但是,更好的优化是在循环外部的单个查询中检索此信息:

    var instellingens = _db.Instellingens
        .Select(q => new { q.INST_LOC_REF, q.INST_LOCNR, q.INST_REF, q.INST_TYPE })
        .Distinct()
        .ToDictionary(q => q, q => true);
    

    (再想,HashSet 在这里最合适,但遗憾的是没有 ToHashSet() 扩展方法。如果你愿意,你可以自己写一个!)

    然后在你的循环中:

    if (instellingens.ContainsKey(new { q.INST_LOC_REF, q.INST_LOCNR,
                                        q.INST_REF, q.INST_TYPE })) {
        // There is no item yet. Create one.
        // ...
    }
    

    然后您可以通过使其延迟检索来优化循环本身:

    // No need for the List<InstellingGegeven>
    foreach (InstellingGegeven i in _db.InstellingGegevens) {
        // ...
    }
    

    【讨论】:

    • +1。显然,Netagitve 方面是内存消耗增加了。除非这很关键,否则这不是真正的否定 - 所以它不会阻止我的 +1。
    【解决方案3】:

    Guffa 所说的,但如果性能是你所追求的,那么在这里使用 Linq 并不是最好的课程。 Linq 和其他 ORM 一样,为了可用性牺牲了性能。对于典型的应用程序执行路径,这通常是一个很好的权衡。另一方面,SQL 非常非常擅长基于集合的操作,所以这确实是一种飞跃的方式。

    【讨论】:

      猜你喜欢
      • 2012-06-12
      • 2011-03-12
      • 1970-01-01
      • 1970-01-01
      • 2011-10-12
      • 1970-01-01
      • 2014-03-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多