通过ID列表搜索数据库中的记录

时间:2019-02-05 10:52:57

标签: c# asp.net asp.net-mvc performance linq

我有一个ID列表(itemID),我将其传递给查询,如下所示:

 using (var ctx = new MyEntities())
            {
                ctx.Configuration.LazyLoadingEnabled = false;
                ctx.Configuration.AutoDetectChangesEnabled = false;
                ctx.Configuration.ValidateOnSaveEnabled = false;
                ctx.Configuration.ProxyCreationEnabled = false;
                var storeItems = ctx.Items.AsNoTracking().Where(y => y.StoreID == 223250).ToList();
                var idList = storeItems.Select(y => y.Id).ToList();
                var storeTransactions = ctx.ItemTransactions.AsNoTracking().Where(r => idList.Contains(r.Id.Value)).ToList();
                return Json("Ok");
            }

要快速总结一下,在这种情况下,例如1001项目的StoreItems列表...

并通过传递1001物料ID来获取物料交易,我得到了

的结果

265000条记录...

两个表之间没有任何关系,但是对Id列进行索引以改善结果...

但是,最后一次查询的性能是绝对糟糕的,并且即使我添加了所有调整以加快速度,也要花一分钟多的时间才能完全完成所有记录的选择,但这仍然无济于事。 ..

有人在获取项目交易时是否还缺少某些东西来加快最后一个查询的速度?

有人可以帮我吗?

3 个答案:

答案 0 :(得分:2)

问题是您将可操作ID列表保存到内存中,然后使用它再次查询。这意味着EF会将其转换为带有所有硬编码的简单IN查询。实际上,我什至怀疑,因为265000条记录的数量太高,它实际上会首先从数据库中获取所有ItemTransactions,然后在内存中进行查询。这会导致性能下降。但是简单的更改将使性能大大提高:

using (var ctx = new MyEntities())
{
    ctx.Configuration.LazyLoadingEnabled = false;
    ctx.Configuration.AutoDetectChangesEnabled = false;
    ctx.Configuration.ValidateOnSaveEnabled = false;
    ctx.Configuration.ProxyCreationEnabled = false;
    var storeItems = ctx.Items.AsNoTracking().Where(y => y.StoreID == 223250);
    var idList = storeItems.Select(y => y.Id); //removed ToList on this and previous line
    var storeTransactions = ctx.ItemTransactions.AsNoTracking()
                               .Where(r => idList.Contains(r.Id.Value)).ToList();
    return Json("Ok");
}

我所做的唯一更改是删除了代码中的ToList()个调用。这将意味着idList是EF可以理解的IQueryable<int>,而不是先执行它,而不会将其用作子查询或在最后一条语句中使用JOIN –这将显着提高性能

还要考虑您是否整体上实际上需要ItemTransactions实例,或者可以对Select使用投影来仅查询您实际需要的属性。这样可以减少必须在您的应用程序和数据库之间传输的数据量。

答案 1 :(得分:1)

无需禁用数据库上的LazyLoadingEnabled选项并使用AsQueryable,这样ids就不会在内存中。在这种情况下,我建议使用join

尝试一下:

using ( var ctx = new MyEntities() )
{
    ctx.Configuration.LazyLoadingEnabled = true;
    ctx.Configuration.AutoDetectChangesEnabled = false;
    ctx.Configuration.ValidateOnSaveEnabled = false;
    ctx.Configuration.ProxyCreationEnabled = false;

    var idList = ctx.Items.AsNoTracking().Where( y => y.StoreID == 223250 ).Select( y => y.Id ).AsQueryable();

    var storeTransactions = ctx.ItemTransactions.AsNoTracking().Where( r => idList.Contains( r.Id.Value ) ).ToList();

    return Json( "Ok" );
}

答案 2 :(得分:1)

您写道:

  

两个表之间没有任何联系

显然存在某种关系。您期望ItemTranaction.Id.Value有时具有Item.Id

的值

您将获得所有StoreId等于223250的商品。从这些商品中获取ID。给我所有具有Id.Value的ItemTransactions,该ID.Value也在获取的ID中。

换句话说:

  

要求:给我所有ItemTransactions,它们的itemTransaction.Id.Value值等于所有StoreId等于223250的项目的ID之一

如果您在一个查询中进行查询,您的查询会更快:

int storeId = 223250;

var result = dbContext.ItemTransactions
  .Join(dbContext.Items.Where(item => item.StoreId == storeId),
  itemTransaction => itemTransaction.Id.Value,
  item => item.Id,
  (itemtransaction, item) => itemTransaction)

换句话说:

  • 将storeTransaction与StoreId等于storId的商品一起加入。
  • 从每个itemTransaction中获取itemTransaction.Id.Value,
  • 从每个项目中获取ID
  • 当这些值相等时,使用itemTransaction和该项创建结果
  • 此结果是itemTransaction

您没有这么说,但是我认为那个item.Id是主键。这样可以确保没有两个具有相同ID的项目,因此所有剩余的itemTransactions都是唯一的。不需要区别。