[英]Linq query timing out, how to streamline query
我们的前端UI具有一个过滤系统,该系统在后端可处理数百万行。 它使用一个在逻辑过程中建立的IQueryable,然后立即执行所有操作。 每个单独的UI组件都进行“与”运算(例如,Dropdown1和Dropdown2将仅返回具有相同选择的行)。 这不是问题。 但是,Dropdown3中包含两种类型的数据,并且需要对选中的项进行“或”运算,然后与其余查询进行“与”运算。
由于要进行大量操作,因此会一直超时。 由于还需要进行其他一些联接,因此有些棘手。 这是我的代码,替换了表名:
//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
等方法来强制它们在内存中执行和评估。
一种方法是创建包含过滤后的ID(根据定义将是唯一的)的Union
查询,并使用join
运算符将其应用于主查询:
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);
另一种方法是使用两个基于OR-ed 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中执行显式联接不是最佳实践,尤其是在您自己设计数据库时。 如果您正在执行数据库的第一代实体,请考虑在表上放置外键约束。 这将允许数据库优先实体生成来拾取它们,并为您提供导航属性,这将大大简化您的代码。
但是,如果您对数据库设计没有任何控制或影响,那么建议您首先使用SQL构造查询以查看其性能。 在那里进行优化,直到获得所需的性能,然后将其转换为使用显式连接作为最后手段的实体框架linq查询。
为了加快此类查询的速度,您可能需要对要加入的所有“关键”列进行索引。 找出需要哪些索引以提高性能的最佳方法,使用EF linq生成的SQL查询,并将其带入SQL Server Management Studio。 从那里开始,更新生成的SQL,以为您的@p参数提供一些预定义的值,仅作为示例。 完成此操作后,右键单击查询,然后使用显示估算的执行计划或包括实际的执行计划。 如果建立索引可以改善查询性能,则很有可能该功能将告诉您有关该索引的信息,甚至可以为您提供创建所需索引的脚本。
在我看来,使用LINQ扩展的实例版本会在完成之前创建多个集合。 使用from语句版本应将其削减很多:
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()
与查询每个驱动程序ID相比,使用groupby扩展还将提供更好的性能。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.