EntityFramework LINQ 查询计数失败但查询 returns 结果。如何优化 LINQ 查询?
EntityFramework LINQ query count fails but query returns result. How to optimize LINQ query?
我有以下执行自左外连接的 LINQ 查询。查询看起来有点复杂,但只是对其自身进行自连接(目的是将每条记录与前一个工作日的记录连接起来),然后进行一些参数化过滤。
var newBreakThreshold = decimal.Parse(WebConfigurationManager.AppSettings["NewBreakThreshold"]);
using (var dbContext = new NavFoToBoCompareDbContext())
{
var query = from current in dbContext.NAVSummaries
let currentWD = SqlFunctions.DatePart("dw", current.ValueDate)
let currentPD = DbFunctions.AddDays(current.ValueDate, currentWD == 2 ? -3 : currentWD == 1 ? -2 : -1).Value
join previous in dbContext.NAVSummaries
on new { current.Portfolio, PD = currentPD }
equals new { previous.Portfolio, PD = previous.ValueDate }
into previousGroup
from previous in previousGroup.DefaultIfEmpty() // LEFT OUTER JOIN
select new { outer = current, inner = previous };
if (dateStart.HasValue)
query = query.Where(e => e.outer.ValueDate >= dateStart.Value);
if (dateEnd.HasValue)
query = query.Where(e => e.outer.ValueDate <= dateEnd.Value);
if (!portfolio.Equals("ALL", StringComparison.OrdinalIgnoreCase))
query = query.Where(e => e.outer.Portfolio.Equals(portfolio, StringComparison.OrdinalIgnoreCase));
if (!owner.Equals("ALL", StringComparison.OrdinalIgnoreCase))
query = query.Where(e => e.outer.PortfolioOwner.Equals(owner, StringComparison.OrdinalIgnoreCase));
if (status != 0)
query = query.Where(e => e.outer.Statuses.Any(s => s.StatusId == status));
var query2 = query.Select(s => new
{
BackOfficeNAV = s.outer.BackOfficeNAV,
FrontOfficeNAV = s.outer.FrontOfficeNAV,
Threshold = s.outer.Threshold,
ExtractId = s.outer.ExtractId,
ExtractStatus = s.outer.ExtractStatus,
PortfolioOwner = s.outer.PortfolioOwner,
DateTimeModified = s.outer.DateTimeModified,
MostCorrectNAV = s.outer.MostCorrectNAV,
Comments = s.outer.Comments,
Statuses = s.outer.Statuses,
Extracts = s.outer.Extracts,
Portfolio = s.outer.Portfolio,
ValueDate = s.outer.ValueDate,
DifferencePercent = s.outer.DifferencePercent,
DayOverDayChange = s.outer.DifferencePercent - s.inner.DifferencePercent,
IsChange = s.inner.DifferencePercent != s.outer.DifferencePercent,
PreviousValueDate = s.inner.ValueDate,
PreviousDifferencePercent = s.inner.DifferencePercent
});
query2 = query2.Where(r => "NEW".Equals(breakOption, StringComparison.OrdinalIgnoreCase) ?
((r.DifferencePercent > r.Threshold) && r.IsChange && r.DayOverDayChange > newBreakThreshold) :
"OLD".Equals(breakOption, StringComparison.OrdinalIgnoreCase) ? (r.DifferencePercent > r.Threshold) :
"ALL".Equals(breakOption, StringComparison.OrdinalIgnoreCase));
var resultCount = query2.Count();
}
查询用在两个地方。在一种方法中,它用于计算分页所需的计数。在另一种方法中,它用于从数据库中获取实际结果。获取更大结果集的实际结果的实现成功执行,而 Count() 查询失败并出现超时异常。 Note:Both 实现完全相同。
有人可以帮我优化这个查询吗?提前致谢。
您可以简单地使用 AsParallel() 启用 linq 查询的多线程执行。
然后您应该检查表的索引以提高性能。
不太确定这是问题所在,但至少让我们尝试通过手动构建表达式消除 dateStart
/ dateEnd
参数来消除所谓的 Parameter Sniffing Problem 的潜在影响具有恒定值。
先来个小帮手方法:
using System;
using System.Linq;
using System.Linq.Expressions;
public static class QueryableUtils
{
public static IQueryable<T> WhereBetween<T>(this IQueryable<T> source, Expression<Func<T, DateTime>> dateSelector, DateTime? startDate, DateTime? endDate)
{
if (startDate == null && endDate == null) return source;
var startCond = startDate != null ? Expression.GreaterThanOrEqual(dateSelector.Body, Expression.Constant(startDate.Value)) : null;
var endCond = endDate != null ? Expression.LessThanOrEqual(dateSelector.Body, Expression.Constant(endDate.Value)) : null;
var predicate = Expression.Lambda<Func<T, bool>>(
startCond == null ? endCond : endCond == null ? startCond : Expression.AndAlso(startCond, endCond),
dateSelector.Parameters[0]);
return source.Where(predicate);
}
}
然后尝试以下方法,看看是否有帮助:
//if (dateStart.HasValue)
// query = query.Where(e => e.outer.ValueDate >= dateStart.Value);
//if (dateEnd.HasValue)
// query = query.Where(e => e.outer.ValueDate <= dateEnd.Value);
query = query.WhereBetween(e => e.outer.ValueDate, dateStart, dateEnd);
我有以下执行自左外连接的 LINQ 查询。查询看起来有点复杂,但只是对其自身进行自连接(目的是将每条记录与前一个工作日的记录连接起来),然后进行一些参数化过滤。
var newBreakThreshold = decimal.Parse(WebConfigurationManager.AppSettings["NewBreakThreshold"]);
using (var dbContext = new NavFoToBoCompareDbContext())
{
var query = from current in dbContext.NAVSummaries
let currentWD = SqlFunctions.DatePart("dw", current.ValueDate)
let currentPD = DbFunctions.AddDays(current.ValueDate, currentWD == 2 ? -3 : currentWD == 1 ? -2 : -1).Value
join previous in dbContext.NAVSummaries
on new { current.Portfolio, PD = currentPD }
equals new { previous.Portfolio, PD = previous.ValueDate }
into previousGroup
from previous in previousGroup.DefaultIfEmpty() // LEFT OUTER JOIN
select new { outer = current, inner = previous };
if (dateStart.HasValue)
query = query.Where(e => e.outer.ValueDate >= dateStart.Value);
if (dateEnd.HasValue)
query = query.Where(e => e.outer.ValueDate <= dateEnd.Value);
if (!portfolio.Equals("ALL", StringComparison.OrdinalIgnoreCase))
query = query.Where(e => e.outer.Portfolio.Equals(portfolio, StringComparison.OrdinalIgnoreCase));
if (!owner.Equals("ALL", StringComparison.OrdinalIgnoreCase))
query = query.Where(e => e.outer.PortfolioOwner.Equals(owner, StringComparison.OrdinalIgnoreCase));
if (status != 0)
query = query.Where(e => e.outer.Statuses.Any(s => s.StatusId == status));
var query2 = query.Select(s => new
{
BackOfficeNAV = s.outer.BackOfficeNAV,
FrontOfficeNAV = s.outer.FrontOfficeNAV,
Threshold = s.outer.Threshold,
ExtractId = s.outer.ExtractId,
ExtractStatus = s.outer.ExtractStatus,
PortfolioOwner = s.outer.PortfolioOwner,
DateTimeModified = s.outer.DateTimeModified,
MostCorrectNAV = s.outer.MostCorrectNAV,
Comments = s.outer.Comments,
Statuses = s.outer.Statuses,
Extracts = s.outer.Extracts,
Portfolio = s.outer.Portfolio,
ValueDate = s.outer.ValueDate,
DifferencePercent = s.outer.DifferencePercent,
DayOverDayChange = s.outer.DifferencePercent - s.inner.DifferencePercent,
IsChange = s.inner.DifferencePercent != s.outer.DifferencePercent,
PreviousValueDate = s.inner.ValueDate,
PreviousDifferencePercent = s.inner.DifferencePercent
});
query2 = query2.Where(r => "NEW".Equals(breakOption, StringComparison.OrdinalIgnoreCase) ?
((r.DifferencePercent > r.Threshold) && r.IsChange && r.DayOverDayChange > newBreakThreshold) :
"OLD".Equals(breakOption, StringComparison.OrdinalIgnoreCase) ? (r.DifferencePercent > r.Threshold) :
"ALL".Equals(breakOption, StringComparison.OrdinalIgnoreCase));
var resultCount = query2.Count();
}
查询用在两个地方。在一种方法中,它用于计算分页所需的计数。在另一种方法中,它用于从数据库中获取实际结果。获取更大结果集的实际结果的实现成功执行,而 Count() 查询失败并出现超时异常。 Note:Both 实现完全相同。
有人可以帮我优化这个查询吗?提前致谢。
您可以简单地使用 AsParallel() 启用 linq 查询的多线程执行。 然后您应该检查表的索引以提高性能。
不太确定这是问题所在,但至少让我们尝试通过手动构建表达式消除 dateStart
/ dateEnd
参数来消除所谓的 Parameter Sniffing Problem 的潜在影响具有恒定值。
先来个小帮手方法:
using System;
using System.Linq;
using System.Linq.Expressions;
public static class QueryableUtils
{
public static IQueryable<T> WhereBetween<T>(this IQueryable<T> source, Expression<Func<T, DateTime>> dateSelector, DateTime? startDate, DateTime? endDate)
{
if (startDate == null && endDate == null) return source;
var startCond = startDate != null ? Expression.GreaterThanOrEqual(dateSelector.Body, Expression.Constant(startDate.Value)) : null;
var endCond = endDate != null ? Expression.LessThanOrEqual(dateSelector.Body, Expression.Constant(endDate.Value)) : null;
var predicate = Expression.Lambda<Func<T, bool>>(
startCond == null ? endCond : endCond == null ? startCond : Expression.AndAlso(startCond, endCond),
dateSelector.Parameters[0]);
return source.Where(predicate);
}
}
然后尝试以下方法,看看是否有帮助:
//if (dateStart.HasValue)
// query = query.Where(e => e.outer.ValueDate >= dateStart.Value);
//if (dateEnd.HasValue)
// query = query.Where(e => e.outer.ValueDate <= dateEnd.Value);
query = query.WhereBetween(e => e.outer.ValueDate, dateStart, dateEnd);