[英]SQL Azure vs. On-Premises Timeout Issue - EF
我正在編寫一份報告,它與我們的本地數據庫運行良好(剛剛從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();
我不能給你一個明確的答案,但我建議通過以下方式解決問題:
看着這條線本身:
where
子句中引用的字段具有索引,例如IsReportable
。 簡答:使用記憶。
答案很長:
由於糟糕的維護計划或有限的硬件,在一個大塊中運行此查詢是導致它在Azure上失敗的原因。 即使不是這種情況,由於您正在使用的所有導航屬性,此查詢將生成數量驚人的連接。 這里的答案是將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();
如果沒有看到實際的表定義,特別是活動實體上的索引和外鍵,很難提出任何建議。
據我所知,Activity(CustomerId,ActivityTypeId,DueDateTime,DispositionId)。 如果這是標准的倉儲表(DateTime,ClientId,Activity),我建議如下:
如果活動數量相當小,則強制使用CONTAINS
var activities = db.Activities.Where(x => x.IsReportable).ToList();
...
.Where(b => activities.Contains(b.Activity))
您甚至可以通過指定您想要ActivityId來幫助優化器。
Activitiy實體的索引應該是最新的。 對於這個特定的查詢我建議(CustomerId,ActivityId,DueDateTime DESC)
預處理表,我的水晶球告訴我它是字典表。
對於類似的任務,為了避免經常點擊Activity表,我創建了另一個小表(CustomerId,LastActivity,LastVAlue)並在狀態發生變化時更新了它。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.