C# SQL数据库中大量记录的Linq查询和Foreach

C# SQL数据库中大量记录的Linq查询和Foreach,c#,entity-framework,linq,C#,Entity Framework,Linq,我正在使用实体框架和Linq。我需要对我的对象的两个属性进行查询 我在数据库中有这个对象,大约有200000条记录: public class DeviceState { public int ID { get; set; } public DateTime TimeStamp { get; set; } public string StatusCode { get; set; } public int Device_ID { get; set; }

我正在使用实体框架和Linq。我需要对我的对象的两个属性进行查询

我在数据库中有这个对象,大约有200000条记录:

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毫秒


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

您可以使用精简的where子句

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

你可以做一个where子句

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

您可以做这些事情来帮助生成查询

只归还你需要的东西。现在您正在返回所有内容,但您只使用了时间戳,所以只需返回它即可。您正在下面设置StatusCode,但是您已经在Where子句中筛选了它,这样您就知道所有返回的项目的StatusCode为xx,因此无需再检索它。这就减少了通过网络返回的数据,减少了将数据映射到对象的周期,减少了数据占用的内存。 您应该查看EF生成的查询。您可以使用sql探查器工具来完成此操作sql Server有一个名为sql探查器的工具。然后查看查询计划,看看是否有任何东西可以像添加缺少的索引一样对您有益。这种分析应该在数据库服务器上完成,而不是在c中。 合并where子句,因为它更容易阅读。 代码

如果因为删除了过滤器而仍然需要状态码,可以这样做

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

您可以做这些事情来帮助生成查询

只归还你需要的东西。现在您正在返回所有内容,但您只使用了时间戳,所以只需返回它即可。您正在下面设置StatusCode,但是您已经在Where子句中筛选了它,这样您就知道所有返回的项目的StatusCode为xx,因此无需再检索它。这就减少了通过网络返回的数据,减少了将数据映射到对象的周期,减少了数据占用的内存。 您应该查看EF生成的查询。您可以使用sql探查器工具来完成此操作sql Server有一个名为sql探查器的工具。然后查看查询计划,看看是否有任何东西可以像添加缺少的索引一样对您有益。这种分析应该在数据库服务器上完成,而不是在c中。 合并where子句,因为它更容易阅读。 代码

如果因为删除了过滤器而仍然需要状态码,可以这样做

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

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

现在,如果需要处理大量数据,那么可能需要在多个线程中并行处理。代码和其他改进:

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

请注意,我没有测试它,我是从内存中写下来的,所以我可能在某个地方打错了。

Igor的答案已经显示了您在任何情况下都应该做的最明显的改进

现在,如果需要处理大量数据,那么可能需要在多个线程中并行处理。代码和其他改进:

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

请注意,我没有测试它,我是从内存中写下来的,所以我可能在某个地方输入了错误。

您多次按where子句进行筛选,因此多次执行相同的操作肯定存在性能问题。因此数据量是一个大问题,在某个时候您必须将其转换为列表,json或类似的东西,所以在那个地方,处理大量数据需要更多的时间。是的,看起来速度更快。foreach是否会对列表中的大量数据造成问题?是的,foreach在大量数据中也会对性能产生影响linq,在这种情况下,foreach比其他foreach更快,我更新的答案与For您也可能要考虑并行使用多线程提供的内循环操作是线程安全的:您是通过WHERE子句多次过滤的,所以它做同一件事情的时候肯定有性能问题,而且数据量也是个大问题。在某些情况下,您必须将其转换为列表、json或类似的内容,因此在该位置,处理大量数据需要花费更多的时间。是的,看起来速度更快。前文会导致列表中大量数据的问题吗?是的,对大数据量的性能也有影响,LINQ,在这种情况下,对于其他的,我更新的答案与For您也可能要考虑并行使用多线程提供的In Load操作是线程安全的: