如何提高此查询速度

本文关键字:速度 查询 何提高 | 更新日期: 2023-09-27 18:19:35

我有一个包含客户列表的表。一个客户有0个、1个或多个合同。

我必须检索所有启用的客户,将它们设置在DTO中,并将当前合同添加到此DTO中(如果有)

目前,它非常缓慢(超过10分钟)。

代码

List<CustomerOverviewDto> result = new List<CustomerOverviewDto>();    
customers= context.Customers.Where(c => c.IsEnabled).ToList();
    foreach (Customer customer in customers)
    {
        CustomerOverviewDto customerDto = GetCustomer(customer);
        Framework.Contract contract =
            customer.Contracts.Where(c => c.ContractEndDate >= DateTime.Today && c.ContractStartDate <= DateTime.Today)
                .FirstOrDefault();
        if (contract != null)
        {
            SetContract(customerDto, contract);
        }
        result.add(customerDto);
    }

如何提高此查询速度

使用投影只返回使用"Select"处理的列。如果你有36列,这会给你更好的结果。

customers= context.Customers.Where(c => c.IsEnabled).Select(cust => new Customer
{
    Id = cust .Id
}).ToList();

https://www.talksharp.com/entity-framework-projection-queries

之后,如果您进行了表扫描或索引扫描,请在查询计划中进行检查。尽量通过设置适当的索引来避免它们。

我认为问题在于在循环中检索契约的查询。最好用这样一个查询检索所有数据:

var date = DateTime.Today;
var query =
    from customer in context.Customers
    where customer => customer.IsEnabled
    select new 
    {
        customer,
        contract = customer.Contracts.FirstOrDefault(c => c.ContractEndDate >= date && c.ContractStartDate <= date)
    };
var result = new List<CustomerOverviewDto>();
foreach (var entry in query)
{
    CustomerOverviewDto customerDto = GetCustomer(entry.customer);
    if (entry.contract != null)
        SetContract(customerDto, entry.contract);
    result.add(customerDto);
}

好的,首先,当你使用.ToList()时,你正在执行查询,并将IsEnabled的每一行都拉回到内存中进行处理。你想在数据库方面做更多的工作。

result = context.Customers.Where(c => c.IsEnabled); //be lazy

其次,只有在有索引可供使用的情况下,查询才会表现良好,并由执行引擎进行适当优化。

在进行比较的字段上添加一些索引。

想想这行代码,例如

    customer.Contracts.Where(c => c.ContractEndDate >= DateTime.Today && 
c.ContractStartDate <= DateTime.Today).FirstOrDefault();

如果您没有从客户到合同的外键,并且在ContractStartDate和ContractEndDate上没有索引,那么它的性能将非常差,并且将为每个"IsEnabled"的客户运行一次

似乎只想在返回值时做一些事情。因此,您可以将其添加到初始查询中,并包括合同:

customers= context.Customers
                           .Include(c => c.Contracts)
                           .Where(c => c.IsEnabled
                                     && c.Contracts.Any(con => con.ContractEndDate >= DateTime.Today && con .ContractStartDate <= DateTime.Today))
                           .ToList();
 foreach (Customer customer in customers)  
 {
    CustomerOverviewDto customerDto = GetCustomer(customer);
    Framework.Contract contract =
    customer.Contracts.Where(c => c.ContractEndDate >= DateTime.Today && c.ContractStartDate <= DateTime.Today)
        .First();
    SetContract(customerDto, contract);
 }

由于我不知道您的域模型结构是什么样子,也不知道为什么不使用导航属性将CURRENT合同映射到客户,因此可以这样做。

通过具体化所有客户和合同,然后将它们在内存中映射到DTO对象,您只需要对数据库执行两次往返操作。假设CustomerId为FK,Customer.Id为PK.

List<CustomerOverviewDto> result = new List<CustomerOverviewDto>();    
customers = context.Customers.Where(c => c.IsEnabled).ToList();
contracts = context.Contracts.Where(c => c.ContractEndDate >= DateTime.Today && c.ContractStartDate <= DateTime.Today).ToList();
foreach (Customer customer in customers)
{
    var customerDto = GetCustomer(customer);
    var contract = contracts.Where(c => c.CustomerId == customer.Id).FirstOrDefault();
    if (contract != null)
    {
        SetContract(customerDto, contract);
    }
    result.add(customerDto);
}

我最终通过使用1个查询和投影解决了问题

context.Customers.Where(c => c.IsEnabled).Select(c => new CustomerOverviewDto{...}).ToList();

我在创建到的CustomerOverviewD时直接检索合同