[英]How to optimize this query using EF
嗨,美好的一天,我是 Entity Framework 的新手。 我只是想知道是否有办法改进我的实施。 这是代码。
public async Task<List<Record>> GetRecordsByBatchId(string batchId, string source)
{
List<string> idList = new List<string>();
//[1] Get all parent ID from table 1 with a filter of source and batchId
var parentIds= await _context.Set<FirstTable>()
.Where(a => a.IsActive
&& a.BatchId.Equals(batchId)
&& a.Source.Equals(source)).Select(b => b.ParentId).ToListAsync();
if (parentIds.Count() == 0)
{
return new List<Record>();
}
//[2] Query idNumber of each parentId from [1] to SecondTable
List<long> idNumber = await _context.Set<SecondTable>()
.Where(a => parentIds.Contains(a.Id))
.Select(b => b.IdNumber).ToListAsync();
//[3] Query Record/s that contains idNumber from previous query [2]. it is possible that 1 or
//more records has same idNumber
List<Risk> recordByIdNumber = await _context.Set<SecondTable>()
.Where(a => idNumber.Contains(a.IdNumber)).ToListAsync();
//[4] In this part I just want to group the records in [3] by Id number and sort each group
//by its endorsementNumber in descending order and return the record with highest endorsement
//number for each group
return (from record in recordByIdNumber
group record by record.IdNumber into g
orderby g.Key
select g.OrderByDescending(risk =>risk.EndorsementNumber).FirstOrDefault()).ToList();
}
}
用于 FirstTable 的model
public class FirstTable
{
public Guid? ParentId{ get; set; }
public string BatchId { get; set; }
public string Source { get; set; }
public bool IsActive { get; set; }
}
用于 SecondTable 的model
public class SecondTable
{
public Guid Id{ get; set; }
public int EndorsementNumber { get; set; }
public long IdNumber { get; set; }
}
注意:我只是在 model 中包含必要的属性。
这种方法按预期工作。 我只想知道是否有可能优化这些查询,使SecondTable表只有 1 个查询。
任何帮助将不胜感激,在此先感谢。
var parentIds = _context.Set<FirstTable>()
.Where(a => a.IsActive
&& a.BatchId.Equals(batchId)
&& a.Source.Equals(source)).Select(b => new { b.parentId });
var risks = await (from s in _context.Set<SecondTable>()
join p in parentIds on s.Id equals p.parentId
join r in _context.Set<SecondTable>() on s.IdNumber equals r.IdNumber
select r).GroupBy(r=>r.IdNumber)
.Select(r=> r.OrderByDescending(risk =>risk.EndorsementNumber).FirstOrDefault())
.ToArrayAsync();
return risks;
您可以有 1 个查询而不是 3 个。随着第一个查询的行数增加,它的性能会更好。
编辑:正如@SvyatoslavDanyliv 在评论中提到的那样,根据 EF 的版本和您使用的提供程序,group-take 操作可能不起作用。 您可能需要按如下操作将查询和分组分开:
var result = await (from s in _context.Set<SecondTable>()
join p in parentIds on s.Id equals p.parentId
join r in _context.Set<SecondTable>() on s.IdNumber equals r.IdNumber
select r).ToArrayAsync();
var risks = result.GroupBy(r=>r.IdNumber)
.Select(r=> r.OrderByDescending(
risk =>risk.EndorsementNumber).FirstOrDefault())
.ToArray();
return risks;
是的,查询 1-3 可以而且应该合并。 为此,您需要在 model 中具有导航属性。 FirstTable和SecondTable之间似乎存在一对多的关系。 让我们改用 Customer 和 Order。
class Customer {
int CustomerId
string BatchId
ICollection<Order> Orders
}
class Order {
int OrderId
int CustomerId
Customer Customer
Risk Risk
}
在这种情况下,您只需将第三个查询编写为
List<Risk> = await _context.Orders.Where(o => o.Customer.BatchId == batchId)
.Select(o => o.Risk).ToListAsync();
显然,我只是在猜测结构和关系。 但希望这可以让你开始。 对我来说Contains()
是“代码气味”。 您的第一个查询很有可能会有很大的列表,并且 contains() 会在数据库中产生一个巨大的IN
子句,这很容易使系统崩溃
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.