比较c#中非常大的数据库对象列表

时间:2015-09-15 21:05:28

标签: c# sql-server asp.net-mvc entity-framework

我继承了设计糟糕的数据库表(没有主键或索引,超大nvarchar字段,日期存储为nvarchar等)。该表有大约350,000条记录。我按预定义的间隔递交了大约2,000个可能的新记录列表,如果数据库还没有匹配的记录,我必须插入任何可能的新记录。

我最初尝试在foreach循环中进行比较,但很快就会发现可能有一种更有效的方法。在做了一些研究之后,我尝试了.Any().Contains().Exclude()方法。

我的研究让我相信.Exclude()方法效率最高,但在尝试时会出现内存错误。 .Any().Contains()方法似乎都需要大致相同的时间才能完成(比foreach循环更快)。

两个列表的结构相同,每个列表包含多个字符串。如果你不介意的话,我有几个我没有找到满意答案的问题。

  1. 比较两个对象列表(由多个字符串组成)时,.Exclude()方法被认为是最有效的吗?
  2. 使用.Exclude()方法时有没有办法使用投影?我想找到一种方法可以实现:

    List<Data> storedData = db.Data;
    List<Data> incomingData = someDataPreviouslyParsed;
    
    // No Projection that runs out of memory
    var newData = incomingData.Exclude(storedData).ToList();
    
    // PsudoCode that I would like to figure out if is possible
    // First use projection on db so as to not get a bunch of irrelevant data
    List<Data> storedData = db.Data.Select(x => new { x.field1, x.field2, x.field3 });
    var newData = incomingData.Select(x => new { x.field1, x.field2, x.field3 }).Exclude(storedData).ToList();
    
  3. 在SQL Server Studio Manager中使用原始SQL语句,查询时间略长于10秒。使用EF,似乎需要超过一分钟。这是EF的优化不当的SQL,还是来自EF的开销会产生这样的差异?

  4. 在这样的情况下,EF中的原始SQL会更好吗?
  5. 半题外话: 从数据库中获取数据并将其存储在变量storedData中时,是否会消除表中存储的任何索引(如果有的话)的用处?

    我讨厌提出这么多问题,而且我确信很多(如果不是全部的话)都是非常无趣的。但是,我无处可去,我一整天都在寻找明确的答案。非常感谢任何帮助。

    更新

    经过进一步研究,我发现这个问题似乎是一个非常好的解决方案。使用EF,我从数据库中获取350,000条记录,只保留创建唯一记录所需的列。然后我获取该数据并将其转换为将保留列作为键的字典(如可以看到here)。这解决了返回数据中已经存在重复的问题,并且给了我一些快速的工作来比较我新解析的数据。性能提升非常明显!

    我仍然不确定这是否接近最佳做法,但我当然可以忍受这种表现。我也看到了一些ToLookup()的引用,我可能会尝试去看看那里是否有性能提升。不过,这里有一些代码来展示我的所作所为:

    var storedDataDictionary = storedData.GroupBy(k => (k.Field1 + k.Field2 + k.Field3 + k.Field4)).ToDictionary(g => g.Key, g => g.First());
    
    foreach (var item in parsedData)
    {
        if (storedDataDictionary.ContainsKey(item.Field1 + item.Field2 + item.Field3 + item.Field4))
        {
            // duplicateData is a previously defined list
            duplicateData.Add(item);
        }
        else
        {
            // newData is a previously defined list
            newData.Add(item);
        }
    }
    

2 个答案:

答案 0 :(得分:0)

  1. 没有理由使用EF。

  2. 如果您要更新或插入记录(那些代表缺少“主键”的记录),则只抓取您决定所需的列。不要浪费其他列的记忆。

  3. 构建现有主键的HashSet(即,如果主键是数字,则为HashSet of int,如果它有多个键 - 将它们组合成字符串)。

  4. 检查你的2000项对抗HashSet,这非常快。

  5. 使用原始sql更新或插入项目。

答案 1 :(得分:0)

我建议你考虑用SQL做,而不是C#。你没有说你正在使用什么RDBMS,但你可以查看MERGE语句,例如(对于SQL Server 2008): https://technet.microsoft.com/en-us/library/bb522522%28v=sql.105%29.aspx

从广义上讲,该语句会检查记录是否为“新” - 如果是,则可以将其插入;如果没有UPDATE和DELETE功能,或者你只是忽略它。