繁体   English   中英

如何使用 EF 优化此查询

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM