如何提高此查询速度
本文关键字:速度 查询 何提高 | 更新日期: 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时直接检索合同