Linq查询和Foreach从SQL数据库中获取大量记录

时间:2016-08-12 10:17:09

标签: c# entity-framework linq

我正在使用Entity Framework和Linq。我需要对我的对象的2个属性进行查询。

我在数据库上有这个对象(大约200.000条记录):

public class DeviceState
{
    public int ID { get; set; }
    public DateTime TimeStamp { get; set; }
    public string StatusCode { get; set; }      
    public int Device_ID { get; set; }
}

我需要这样做一个查询:

List<DeviceState> listState = systemDB.DeviceStates.Where(s => s.Device_ID == DeviceID).Where(l => l.TimeStamp > startDate).Where(l => l.TimeStamp < endDate).Where(s => s.StatusCode == "xx").ToList();
foreach (DeviceState status in listState)
{  
   // here I need to save in an object the status code and the time stamp:
   object.StatusCode= status.StatusCode;
   object.TimeStamp = status.TimeStamp; 
}

此查询需要很长时间(约15分钟)。我认为这是由于列表的创建。所以我尝试了这个:

foreach (DeviceState status in systemDB.DeviceStates.Where(s => s.Device_ID == DeviceID).Where(l => l.TimeStamp > startDate).Where(l => l.TimeStamp < endDate).Where(s => s.StatusCode == "xx"))
{  
   // here I need to save in an object the status code and the time stamp:
   object.StatusCode= status.StatusCode;
   object.TimeStamp = status.TimeStamp; 
}

创建列表的速度要快得多。但由于foreach循环,我仍然存在性能问题。每个元素需要5毫秒。

我需要找到一个需要几秒钟才能执行的解决方案。

3 个答案:

答案 0 :(得分:3)

您可以执行这些操作来帮助生成查询。

  1. 只返回您需要的东西。现在你要返回一切但是你只使用TimeStamp所以只需返回它。您正在设置StatusCode,但是您已经在Where子句中对此进行了过滤,因此您知道所有返回的项目都有StatusCode&#34; xx&#34;所以也不需要检索它。这样可以减少通过网络返回的数据,减少将数据映射到对象所需的周期,以及减少数据占用的内存。
  2. 您应该查看EF生成的查询。您可以使用sql profiler工具执行此操作(Sql Server有一个名为Sql Profiler的工具)。然后查看查询计划,看看是否有什么可以帮助您添加缺少的索引。此分析应在数据库服务器上完成,而不是在c#。
  3. 中完成
  4. 合并您的where子句,因为它更容易阅读。
  5. 代码

    // list of timestamps from database
    var timeStamps = systemDB.DeviceStates.Where(s => s.Device_ID == DeviceID && 
                                                      s.TimeStamp > startDate && 
                                                      s.TimeStamp < endDate &&
                                                      s.StatusCode == "xx")
                                          .Select(x => x.TimeStamp).ToList();
    

    如果你仍然想要你的状态代码,因为你删除了过滤器,你就可以这样做

    var timeStamps = systemDB.DeviceStates.Where(s => s.Device_ID == DeviceID &&
                                                      s.TimeStamp > startDate && 
                                                      s.TimeStamp < endDate && 
                                                      s.StatusCode == "xx")
                                          .Select(x => new {x.TimeStamp, x.StatusCode})
                                          .ToList();
    

答案 1 :(得分:1)

你可以在子句

中做一个瘦身
List<DeviceState> listState = systemDB.DeviceStates.Where(
                                  l => l.Device_ID == DeviceID 
                               && l.TimeStamp > startDate
                               && l.TimeStamp < endDate 
                               && l.StatusCode == "xx" 
                               ).ToList();

然后使用for而不是foreach,因为foreach在大量数据的情况下会更慢

for (int i = 0; i< listState.Count; i++ )
{  
   object.StatusCode= listState[i].StatusCode;
   object.TimeStamp = listState[i].TimeStamp; 
}

答案 2 :(得分:1)

Igor的回答已经显示了在任何情况下你应该做的最明显的改进。

现在,如果您手动处理大量数据,那么您可能希望在与System.Threading.Tasks.Parallel.ForEach并行的多个线程中执行此操作。代码(有其他改进):

var deviceStateTime = systemDB.DeviceStates.Where(s => s.Device_ID == DeviceID
                        && s.TimeStamp > startDate)
                        && s.TimeStamp < endDate
                        && s.StatusCode == "xx");
Parallel.ForEach(deviceStateTime, (time) =>
                 {
                    object.StatusCode = "xx";
                    object.Timestamp = time;
                 });

请注意,我没有测试它(我是从记忆中写下来的),所以我可能在某个地方输错了。