首页 > 解决方案 > 在本地列表和表之间使用 LINQ to SQL Contains() 的最佳方式

问题描述

在本地列表和数据库中的表中查找常用项目的更好方法是什么?

物品或物品2?

var clientId = 2;
// members is a local list, but not anonymous, like this
var members = new[] { 
    new { MemberId = "1", ... }, 
    new { MemberId = "2", ... }, 
    new { MemberId = "3", ... },
    new { MemberId = "4", ... },
    new { MemberId = "5", ... },// so on...
};

var items = db.MemberTable.Where(x => members.Select(s => s.MemberId).Contains(x.MemberId) && x.StoreId == storeId).Select(i => i.MemberId).ToList();

var items2 = members.Where(x => db.MemberTable.Where(c => c.StoreId== storeId).Select(s => s.MemberId).Contains(x.MemberId)).Select(i => i.MemberId).ToList();

标签: c#linq-to-sql

解决方案


LINQ to SQL 或 LINQ to EF 并不神奇。他们不能做任何不能用 SQL 表达的事情。他们也不会在生成 SQL 语句之前将错误的查询重写为更好的形式。

不幸的是,当 L2S(以及一些早期版本的 EF Core)无法生成正确的 SQL 语句时,它们会尝试加载行并在客户端上处理它们,从而导致查询效率非常低。

根据 ID 列表查询项目的方式是:

select ID
from Members
Where ID in (1,2,6,7,9)

LINQ to EF/SQL只能在 Enumerable.Contains 用于 ID 列表时生成IN子句,例如:

var ids=new[]{1,2,6,7,9};
var actualIDs = db.MemberTable.Where(x=>ids.Contains(x.ID));

第一个查询以详细的方式执行此操作。本质上和写一样

var ids=members.Select(s => s.MemberId);
var items = db.MemberTable.Where(x => ids.Contains(x.MemberId) && x.StoreId == storeId)
                          .Select(i => i.MemberId)
                          .ToList();

这应该生成这个查询:

SELECT MemberID
From Members
Where StoreId=12354 and MemberID in (1,2,5,8,9...)

这是一个快速查询,特别是如果MemberIDStoreID被索引。

如果使用 LINQ to SQL,则可能需要向本地查询添加ToArray()或。ToList()L2S 至少可以说是非常挑剔的,而且从来都只是一个技术演示。无论如何,本地性能是相同的,在这两种情况下,都需要读取所有 ID 来构造 IN 子句:

var ids=members.Select(s => s.MemberId).ToArray();

第二个查询可能 - 它的作用并不明显,因为所有内容都写在一行中,并且无法查看哪一部分是数据库查询,哪一部分是本地查询。重写它:

var items2 = members.Where(x => 
                  db.MemberTable
                     .Where(c => c.StoreId== storeId)
                     .Select(s => s.MemberId)
                .Contains(x.MemberId))
              .Select(i => i.MemberId)
              .ToList();

表明这对成员列表中的每个项目执行一次此数据库查询:

db.MemberTable
  .Where(c => c.StoreId== storeId)
  .Select(s => s.MemberId)

必须跟踪 SQL 语句,但我怀疑这将一遍又一遍地执行以下查询:

select MemberID
from Members
where StoreID=12345

将其重写为:

var dbIDs=b.MemberTable
           .Where(c => c.StoreId== storeId)
           .Select(s => s.MemberId)
           .ToArray();
var items2 = members.Where(x => dbIds.Contains(x.MemberId))
              .Select(i => i.MemberId)
              .ToList();

这仍然会加载不需要的行并占用额外的数据库锁,但至少它只会这样做一次。


推荐阅读