【问题标题】:Improving EF Query Performance - checking if rows exist in a prefetched List<string>提高 EF 查询性能 - 检查预取 List<string> 中是否存在行
【发布时间】:2017-03-19 10:18:15
【问题描述】:

我有以下(通用)查询:

var listOfPossibleCars= new List<string>();
var listOfCars = db.Cars.Where(s => listOfPossibleCars.Contains(s.CarName)).ToList();

但listOfPossibleCars 很大,任何包含数十万条记录。此查询导致延迟,我想知道如何改进它。

【问题讨论】:

  • listOfPossibleCars 总是一样的,还是经常变化(每次调用)?
  • @Evk 每次都不一样,而且每次都很大
  • 整个 Cars 表有多大?将它带入内存是否可行?否则,我建议您使用 SqlBulkIncert 进入的临时表,然后像@Evik 回答的那样进行连接,不过,我建议您使用 2 个表,1 个带有查询 ID,然后将其插入到 Filter 表中(int FilterID,varchar 值)。因此,您可以唯一地识别每个过滤器,而无需锁定它,以免有人尝试同时更新它
  • @MichalCiechan 数百万行,所以不幸的是内存不是一个选项。

标签: c# .net entity-framework linq


【解决方案1】:

不要使用内存中的集合来执行此操作,而是将listOfPossibleCars 的数据插入到具有正确定义的索引和分区的临时表中,并在数据库中发生这种情况。使用正确定义的表,数据库将执行散列连接。

数据进入数据库后,查询将如下所示:

//Instead data to database
from car in db.Cars
join possibleCar in db.PossibleCars on car.CarName equals possibleCar.CarName
select car;

【讨论】:

  • 问题是我需要使用 EF 对这些实体进行更改跟踪...知道如何使用 EF 查询来实现这一点吗?
  • 将列表插入数据库只是为了让数据库可以更有效地进行过滤。以后你想用cars 做任何事情,你仍然可以做
  • 请注意,OP 进行实体框架查询,而不是 LINQ 到对象,因此将 List 更改为 HashSet 将没有效果。
  • 你可能会认为我因为那个 HashSet 而被否决了,但事实并非如此,真正的否决者很可能根本不会看到你的评论......
  • 我强烈推荐使用 SqlBulkInsert 而不是标准的 EF Insert (Add(...))。我还建议添加一个 StringFilter(int Id 标识,varchar 名称)和一个 StringFilterValue(int FilterId,varchar 值)。在 StringFilter 中插入一行以获取 AutoID,然后将 BCP(SqlBulkInsert) 与过滤器 ID 和所有汽车一起放入 FilterValue 表中。对于 100k 的行,这需要几秒钟(如果不是子)
【解决方案2】:

如果 listOfPossibleCars 是相对静态的(根本不更改或至少不经常更改),您确实可以将其放入数据库中,如另一个答案中所述。如果不是这种情况并假设您使用 sql server,则可以使用表值参数。请注意,Entity Framework 使用 Contains 非常慢,尤其是在长列表中。

首先在数据库中创建自定义表类型:

CREATE TYPE MyType AS TABLE 
(
    CarName varchar(200) primary key    
)

然后像这样查询(注意这是我脑子里的代码,没有经过测试,所以询问是否有什么不顺利):

var listOfPossibleCars = new List<string>();
var dt = new DataTable();
dt.Columns.Add("CarName");
foreach (var car in listOfPossibleCars) {
     dt.Rows.Add(car);
}
var possibleCars = new SqlParameter("possibleCars", SqlDbType.Structured);
possibleCars.Value = dt;
possibleCars.TypeName = "dbo.MyType";

var listOfCars = db.Cars.SqlQuery("select C.* from Cars C inner join @possibleCars P on C.CarName = P.CarName", possibleCars).ToList();

是的,您必须使用原始 sql 查询,但如果您的列表非常庞大 - 您无法仅使用实体框架有效地进行此类查询。

【讨论】:

  • 使用 SqlQuery 检索是否保留更改跟踪?
  • Database.SqlQuery 返回未跟踪的实体。但是,您可以使用返回跟踪实体的 DbSet.SqlQuery(因此,db.Cars.SqlQuery)。请参阅我的更新答案。
  • @MichalCiechan 我们根本没有在这里插入任何东西,你在说什么?
猜你喜欢
  • 2020-12-15
  • 1970-01-01
  • 1970-01-01
  • 2013-05-15
  • 2022-01-09
  • 2013-08-03
  • 2019-12-29
  • 2021-11-22
相关资源
最近更新 更多