优化代码:Linq和foreach循环15k记录

时间:2010-09-06 13:49:38

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

这是我的代码

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();

3 个答案:

答案 0 :(得分:3)

在存储过程中执行此操作会快得多。我们做了一些非常相似的事情,表中只有大约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从您的计时器执行该过程。

答案 1 :(得分:2)

您可以优化此方法的一种方法是将Count(...) <= 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.
    // ...
}

然后你可以通过使它变为lazy-retrieve来优化循环本身:

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

答案 2 :(得分:1)

Guffa说的话,但如果你的表现与你所追求的一样,那么在这里使用Linq并不是最好的方法。与所有其他ORM一样,Linq牺牲了可用性。对于典型的应用程序执行路径,这通常是一个很好的权衡。另一方面,SQL非常非常擅长基于集合的操作,因此真的是在这里飞行的方式。