我有一个如下表
Employee
-------------------
id role
1 a
2 a
3 b
4 c
5 b
----------------------
我拥有的过滤器是ids {1,3,5} and roles {a,b}
所以我需要获取具有id=1 and role=a , id=2 and role=b , id=3 and role=a, id=3 and role=b etc...
的行
基本上id * roles
是我需要从数据库中获取的。
我的项目中使用的当前代码如下
foreach (int role in roles)
{
foreach (int id in ids)
{
//get the data using id and role and append to a list
}
}
并且每次迭代都会发生数据库命中,因此这会造成巨大的永久性问题。因此,我正在尝试改进已有的代码,并已用以下代码替换了foreach。
var itemsTobeDeleted = context.TableName.Where
(
item =>
obj.ids.Contains(item.Id) &&
obj.roles.Contains(item.Role)
).ToList();
我的问题是这是我的新查询将给出与每个给定相同的结果吗? 如果是,那么性能如何?
还是我们有更好的方法来做同样的事情?
答案 0 :(得分:1)
是的,您的第二个解决方案更有效!
您使用DbContext访问表。显然,该表位于数据库中。数据库管理系统在查询数据方面进行了非常优化。
但是,查询中较慢的部分之一是查询和所选数据在流程与DBMS之间的传输。因此,限制这一点是明智的。
您的foreach方法将对每个[role,id]组合执行一次查询。这是非常低效的。您的第二个方法将只执行一个查询,并且仅返回一个结果序列。这样更有效。
添加:
伊万·斯托夫(Ivan Stoev)向我展示了下面的方法不起作用:与IEnumerable相比,IQueryable.Contains只能处理基本类型。因此,下面的“包含”将不起作用。
因此,以下内容不能作为IQueryable使用。
可以对该查询进行一些优化:让您的流程创建所需的[role,id]组合的序列,并在数据库中查询与该组合匹配的所有TableNames
。
class RoleIdCombination
{
public char Role {get; set;}
public int Id {get; set;}
}
// let your process create the requested RoleIdCombinations:
var roleIdCombinations = CreateRoleIdCombinations(roles, ids);
// do only one query:
var result = dbContext.TableNames
.Select(tableName => new
{
// for easier equality check: make a RoleIdCombination:
RoleIdCombination = new RoleIdCombination
{
Role = tableName.Role,
Id = tableName.Id,
}
// remember the original item
TableName = tableName,
})
.Where(item => roleIdCombinations.Contains(item.RoleIdCombination));
所以现在您知道您的数据库管理系统将只枚举您的表一次。
功能CreateRoleIdCombinations
:
IEnumerable<RoleIdCombination> CreateRoleIdCombinations(
IEnumerable<char> roles,
IEnumerable<int> ids)
{
foreach (var role in roles)
{
foreach (var id in ids)
{
yield return new RoleIdCombination
{
Role = role,
Id = id,
};
}
}
}
为此,我们需要(角色数量* id数量)枚举。我希望它会比表中的项目数小得多,毕竟,您不会每次都删除表的一半吗?
答案 1 :(得分:0)
如果将集合更改为 set ,则包含的成本为O(1),因此,性能更好的则为 List.Contains (O(n) )。
HashSet<int> ids= new HashSet<int>{ 1,3,5 };