Linq查询超时,如何简化查询

问题描述:

我们的前端UI有一个过滤系统,在后端运行数百万行。它使用在逻辑过程中构建的IQueryable,然后一次执行所有内容。每个单独的UI组件都进行了与运算(例如,Dropdown1和Dropdown2将只返回具有共同选定内容的行)。这不是问题。但是,Dropdown3中有两种类型的数据,检查的项目需要ORd在一起,然后与查询的其余部分进行与运算。Linq查询超时,如何简化查询

由于大量的行正在进行操作,它会保持超时。由于需要发生一些额外的连接,所以这有点棘手。这里是我的代码,用表名称进行替换:

//The end list has driver ids in it--but the data comes from two different places. Build a list of all the driver ids. 
driverIds = db.CarDriversManyToManyTable.Where(
         cd => 
          filter.CarIds.Contains(cd.CarId) && //get driver IDs for each car ID listed in filter object 
          ).Select(cd => cd.DriverId).Distinct().ToList(); 

driverIds = driverIds.Concat(
        db.DriverShopManyToManyTable.Where(ds => filter.ShopIds.Contains(ds.ShopId)) //Get driver IDs for each Shop listed in filter object 
         .Select(ds => ds.DriverId) 
         .Distinct()).Distinct().ToList(); 
//Now we have a list solely of driver IDs 

//The query operates over the Driver table. The query is built up like this for each item in the UI. Changing from Linq is not an option. 
query = query.Where(d => driverIds.Contains(d.Id)); 

我如何可以简化这个查询,使我没有找回ID的成千上万到内存中,然后给他们回SQL?

有几种方法可以生成单个SQL查询。他们需要保留IQueryable<T>类型的查询的部分,即请勿使用ToList,ToArray,AsEnumerable等方法强制它们在内存中执行和评估。

一种方法是创建一个包含过滤IDS(这将是通过定义唯一的),并使用join操作者施加它的主查询Union查询:

var driverIdFilter1 = db.CarDriversManyToManyTable 
    .Where(cd => filter.CarIds.Contains(cd.CarId)) 
    .Select(cd => cd.DriverId); 
var driverIdFilter2 = db.DriverShopManyToManyTable 
    .Where(ds => filter.ShopIds.Contains(ds.ShopId)) 
    .Select(ds => ds.DriverId); 
var driverIdFilter = driverIdFilter1.Union(driverIdFilter2); 
query = query.Join(driverIdFilter, d => d.Id, id => id, (d, id) => d); 

另一种方式,可以使用两种或 - 编Any基础条件,这将转化为EXISTS(...) OR EXISTS(...) SQL查询过滤器:

query = query.Where(d => 
    db.CarDriversManyToManyTable.Any(cd => d.Id == cd.DriverId && filter.CarIds.Contains(cd.CarId)) 
    || 
    db.DriverShopManyToManyTable.Any(ds => d.Id == ds.DriverId && filter.ShopIds.Contains(ds.ShopId)) 
); 

你可以试试,看看哪一个性能更好。

这个问题的答案很复杂,并且有很多方面可以单独帮助您解决您的具体问题。

首先,考虑使用分页。 .Skip(PageNum * PageSize).Take(PageSize)我怀疑你的用户需要在前端一次查看数百万行。只向他们展示100个,或者其他较小的数字对你来说似乎是合理的。

你已经提到你需要使用连接来获取你需要的数据。这些连接可以在形成你的IQueryable(实体框架)时完成,而不是在内存中(linq到对象)。阅读linq中的join语法。但是,在LINQ中执行显式连接并不是最佳实践,尤其是在您自己设计数据库的情况下。如果您正在执行数据库第一代实体,请考虑在您的表上放置外键约束。这将允许数据库优先实体生成选择它们并为您提供导航属性,这将大大简化您的代码。

但是,如果您对数据库设计没有任何控制权或影响力,那么我建议您先在SQL中构建您的查询以查看其执行方式。在那里进行优化直到获得所需的性能,然后将其转换为实体框架linq查询,该查询使用显式连接作为最后的手段。

要加快查询速度,您可能需要对所有要加入的“关键”列执行索引。找出需要提高性能的索引的最佳方法是,将由EF linq生成的SQL查询带入SQL Server Management Studio。从那里,更新生成的SQL,为您的@p参数提供一些预定义的值,仅作为示例。完成此操作后,右键单击查询并使用显示预计执行计划或包含实际执行计划。如果索引可以提高您的查询性能,那么这个功能很有可能会告诉您这个问题,甚至为您提供脚本来创建所需的索引。

在我看来,使用实例版本的LINQ扩展在创建完成之前创建了多个集合。使用从声明版本应该削减相当多:

driveIds = (from var record in db.CarDriversManyToManyTable 
      where filter.CarIds.Contains(record.CarId) 
      select record.DriverId).Concat 
      (from var record in db.DriverShopManyToManyTable 
      where filter.ShopIds.Contains(record.ShopId) 
      select record.DriverId).Distinct() 

同样使用groupby扩展会比查询每个驱动程序Id更好的性能。