我正在处理一份报告,该报告与我们的本地数据库(刚刚从PROD刷新)运行良好。但是,当我将站点部署到Azure时,我在执行期间获得了SQL Timeout。如果我将我的开发实例指向SQL Azure实例,我也会得到超时。
目标:要输出已在搜索范围内创建活动的客户列表,以及何时找到该客户,请获取有关该客户的有关政策等的其他信息。我已删除了下面的一些属性为了简洁(尽我所能)......
经过大量的反复试验,只要不执行此代码块,我就可以在1000MS内完全一致地运行整个查询。
CurrentStatus = a.Activities
.Where(b => b.ActivityType.IsReportable)
.OrderByDescending(b => b.DueDateTime)
.Select(b => b.Status.Name)
.FirstOrDefault(),
有了这些代码,事情开始变得混乱。我认为Where子句是其中很重要的一部分:.Where(b => b.ActivityType.IsReportable)
。获取状态名称的最佳方法是什么?
有关为什么SQL Azure会超时而内部部署的任何想法都会在不到100MS的时间内解决这个问题?
return db.Customers
.Where(a => a.Activities.Where(
b => b.CreatedDateTime >= search.BeginDateCreated
&& b.CreatedDateTime <= search.EndDateCreated).Count() > 0)
.Where(a => a.CustomerGroup.Any(d => d.GroupId== search.GroupId))
.Select(a => new CustomCustomerReport
{
CustomerId = a.Id,
Manager = a.Manager.Name,
Customer = a.FirstName + " " + a.LastName,
ContactSource= a.ContactSource!= null ? a.ContactSource.Name : "Unknown",
ContactDate = a.DateCreated,
NewSale = a.Sales
.Where(p => p.Employee.IsActive)
.OrderByDescending(p => p.DateCreated)
.Select(p => new PolicyViewModel
{
//MISC PROPERTIES
}).FirstOrDefault(),
ExistingSale = a.Sales
.Where(p => p.CancellationDate == null || p.CancellationDate <= myDate)
.Where(p => p.SaleDate < myDate)
.OrderByDescending(p => p.DateCreated)
.Select(p => new SalesViewModel
{
//MISC PROPERTIES
}).FirstOrDefault(),
CurrentStatus = a.Activities
.Where(b => b.ActivityType.IsReportable)
.OrderByDescending(b => b.DueDateTime)
.Select(b => b.Disposition.Name)
.FirstOrDefault(),
CustomerGroup = a.CustomerGroup
.Where(cd => cd.GroupId == search.GroupId)
.Select(cd => new GroupViewModel
{
//MISC PROPERTIES
}).FirstOrDefault()
}).ToList();
答案 0 :(得分:4)
我不能给你一个明确的答案,但我建议通过以下方式解决问题:
看看这条线本身:
where
子句中引用的字段具有索引,例如IsReportable
。答案 1 :(得分:1)
简短回答:使用记忆。
答案很长:
由于糟糕的维护计划或有限的硬件,在一个大块中运行此查询是导致它在Azure上失败的原因。即使情况并非如此,由于您使用的所有导航属性,此查询将生成数量惊人的连接。这里的答案是将Azure 可以运行的较小部分分解。我将尝试将您的查询重写为使用.NET应用程序内存的多个更小,更易于摘要的查询。请耐心等待我(或多或少)对您的业务逻辑/数据库架构进行有根据的猜测并相应地重写查询。很抱歉使用LINQ的查询表单,但我发现join
和group by
等内容在该表单中更具可读性。
var activityFilterCustomerIds = db.Activities
.Where(a =>
a.CreatedDateTime >= search.BeginDateCreated &&
a.CreatedDateTime <= search.EndDateCreated)
.Select(a => a.CustomerId)
.Distinct()
.ToList();
var groupFilterCustomerIds = db.CustomerGroup
.Where(g => g.GroupId = search.GroupId)
.Select(g => g.CustomerId)
.Distinct()
.ToList();
var customers = db.Customers
.AsNoTracking()
.Where(c =>
activityFilterCustomerIds.Contains(c.Id) &&
groupFilterCustomerIds.Contains(c.Id))
.ToList();
var customerIds = customers.Select(x => x.Id).ToList();
var newSales =
(from s in db.Sales
where customerIds.Contains(s.CustomerId)
&& s.Employee.IsActive
group s by s.CustomerId into grouped
select new
{
CustomerId = grouped.Key,
Sale = grouped
.OrderByDescending(x => x.DateCreated)
.Select(new PolicyViewModel
{
// properties
})
.FirstOrDefault()
}).ToList();
var existingSales =
(from s in db.Sales
where customerIds.Contains(s.CustomerId)
&& (s.CancellationDate == null || s.CancellationDate <= myDate)
&& s.SaleDate < myDate
group s by s.CustomerId into grouped
select new
{
CustomerId = grouped.Key,
Sale = grouped
.OrderByDescending(x => x.DateCreated)
.Select(new SalesViewModel
{
// properties
})
.FirstOrDefault()
}).ToList();
var currentStatuses =
(from a in db.Activities.AsNoTracking()
where customerIds.Contains(a.CustomerId)
&& a.ActivityType.IsReportable
group a by a.CustomerId into grouped
select new
{
CustomerId = grouped.Key,
Status = grouped
.OrderByDescending(x => x.DueDateTime)
.Select(x => x.Disposition.Name)
.FirstOrDefault()
}).ToList();
var customerGroups =
(from cg in db.CustomerGroups
where cg.GroupId == search.GroupId
group cg by cg.CustomerId into grouped
select new
{
CustomerId = grouped.Key,
Group = grouped
.Select(x =>
new GroupViewModel
{
// ...
})
.FirstOrDefault()
}).ToList();
return customers
.Select(c =>
new CustomCustomerReport
{
// ... simple props
// ...
// ...
NewSale = newSales
.Where(s => s.CustomerId == c.Id)
.Select(x => x.Sale)
.FirstOrDefault(),
ExistingSale = existingSales
.Where(s => s.CustomerId == c.Id)
.Select(x => x.Sale)
.FirstOrDefault(),
CurrentStatus = currentStatuses
.Where(s => s.CustomerId == c.Id)
.Select(x => x.Status)
.FirstOrDefault(),
CustomerGroup = customerGroups
.Where(s => s.CustomerId == c.Id)
.Select(x => x.Group)
.FirstOrDefault(),
})
.ToList();
答案 2 :(得分:0)
很难在没有看到实际表定义的情况下提出任何建议,特别是活动实体上的索引和外键。
据我所知,Activity(CustomerId,ActivityTypeId,DueDateTime,DispositionId)。如果这是标准的仓储表(DateTime,ClientId,Activity),我建议如下:
如果活动数量相当小,则强制使用CONTAINS
var activities = db.Activities.Where(x =&gt; x.IsReportable)。ToList();
...
.Where(b =&gt; activities.Contains(b.Activity))
您甚至可以通过指定您想要ActivityId来帮助优化器。
Activitiy实体的索引应该是最新的。对于这个特定的查询,我建议(CustomerId,ActivityId,DueDateTime DESC)
预处理处理表,我的水晶球告诉我它是字典表。
对于类似的任务,为了避免经常点击Activity表,我创建了另一个小表(CustomerId,LastActivity,LastVAlue),并在状态发生变化时对其进行了更新。