简体   繁体   English

优化慢LINQ查询

[英]Optimizing a slow LINQ query

I have a LINQ query that I am having trouble optimizing and takes about 5.5 seconds to run. 我有一个LINQ查询,我无法优化,大约需要5.5秒才能运行。 I am using a view called StaffingResourceData and a table called StaffingForecasts. 我正在使用一个名为StaffingResourceData的视图和一个名为StaffingForecasts的表。

Each StaffingResource has a ResourceId, a Division, and a Type. 每个StaffingResource都有一个ResourceId,一个Division和一个Type。 A StaffingForecast has a ResourceId, Project, Date (represents a Monday of a week), Hours. StaffingForecast有一个ResourceId,Project,Date(表示一周的星期一),Hours。 A StaffingResource can have 0-many StaffingForecasts. StaffingResource可以有0多个StaffingForecasts。

For each StaffingResource, I need a list of their total forecasted hours for the next 12 weeks. 对于每个StaffingResource,我需要一份他们未来12周的总预测时间列表。 Here is what I have right now: 这就是我现在所拥有的:

// Get list of dates
var dates = new List<DateTime>();
var start = Utilities.GetStartOfWeek(DateTime.Today);
for (var i = 0; i < 12; i++)
{
    dates.Add(start.AddDays(i * 7));
}
var end = dates[11];

// Get resources
var resources = (from r in context.StaffingResourceDatas
                 where r.EmployeeId != null
                     && !exclusionList.Contains(r.ResourceTitleId)
                 join f in context.StaffingForecasts.Where(x => x.Date >= start && x.Date <= end) on r.ResourceId equals f.ResourceId into g1
                 from f in g1.DefaultIfEmpty()
                 group new { f.Date, f.Hours } by r into g2
                 select new ChartResourceModel
                 {
                     ResourceId = g2.Key.ResourceId,
                     Division = g2.Key.ResourceDivision,
                     Type = g2.Key.ResourceType,
                     Dates = dates.Select(d => new ChartDateModel
                     {
                         Date = d,
                         Available = (g2.Where(f => f.Date == d).Any() ? g2.Where(f => f.Date == d).Sum(f => f.Hours) : 0) < 24
                     }).ToList()
                 })
               .ToList();

Any ideas on how I could speed this up? 关于如何加快速度的任何想法?

  1. Avoid using Contains . 避免使用Contains It degrades performance heavily. 它严重降低了性能。 See this post 看这篇文章

  2. ToList() is a command to execute your query. ToList()是执行查询的命令。 Till you call ToList() method, linq query is not started as linq has a feature called deferred execution . 直到调用ToList()方法,linq查询才会启动,因为linq具有一个名为延迟执行的功能。 So if you call ToList() , you start some real operations with Databaseof files. 因此,如果您调用ToList() ,则可以使用Databaseof文件启动一些实际操作。

  3. reducing columns of table reduces bandwidth required(delete unnecessary columns from your query) 减少表的列可减少所需的带宽(从查询中删除不必要的列)
  4. turn off change-tracking and identity-management (for example, ObjectTrackingEnabled in LINQ-to-SQL) 关闭变更跟踪和身份管理(例如,LINQ-to-SQL中的ObjectTrackingEnabled)

     using (YourDataContext dataContext = new YourDataContext()) { dataContext.ObjectTrackingEnabled = false; //Your code } 
  5. Use one of the tuning options of EF such as .AsNoTracking() . 使用EF的一个调整选项,例如.AsNoTracking() The extensive description can be seen here. 这里可以看到广泛的描述。
  6. use a pre-compiled query. 使用预编译的查询。 It sometimes reduces pre-processing overheads 它有时会减少预处理开销

After playing around with it for awhile, I was able to get the loading time down from 5.5 seconds to 1.5 seconds. 在玩了一段时间后,我能够将加载时间从5.5秒降低到1.5秒。 Here is what I came up with: 这是我想出的:

// Get resources
var resources = (from r in
                    (from r in context.StaffingResourceDatas
                     where r.EmployeeId != null
                         && !exclusionList.Contains(r.ResourceTitleId)
                     join f in context.StaffingForecasts on r.ResourceId equals f.ResourceId
                     group f by r into g
                     select new
                     {
                         Resource = g.Key,
                         Forecasts = g.Where(f => f.Date >= start && f.Date <= end && f.StaffingPotentialProject == null).ToList()
                     }).ToList()
                 group r.Forecasts by r.Resource into g
                 select new ChartResourceModel
                 {
                     ResourceId = g.Key.ReportsToId,
                     Division = g.Key.ResourceDivision,
                     Type = g.Key.ResourceType,
                     Dates = dates.Select(d => new ChartDateModel
                     {
                         Date = d,
                         Available = (g.SelectMany(f => f.Where(x => x.Date == d)).Sum(x => x.Hours)) < 24
                     }).ToList()
                 }).ToList();

It seems like the best way is to just get all the data you need and calling .ToList() without trying to do anything fancy and then performing any extra operations on that data. 看起来最好的方法是获取所需的所有数据并调用.ToList()而不尝试做任何奇特的事情,然后对该数据执行任何额外的操作。

For start, try to avoid ".ToList()" unless everything is done, because when you fire ".ToList()", result is being materialized, which you don't want if there is a lot of data and you want to do more query operations on that data. 首先,尽量避免“。ToList()”,除非一切都已完成,因为当您触发“.ToList()”时,结果正在实现,如果有大量数据并且您想要对该数据执行更多查询操作。

So, try with IQueryable properties, so you can at least get data much faster and then do some operation on it. 因此,尝试使用IQueryable属性,这样您至少可以更快地获取数据,然后对其进行一些操作。

And of course, check what is with query/queries you are sending to SQL. 当然,检查您要发送给SQL的查询/查询是什么。 Maybe columns you are searching are not properly indexed(!?) or you don't have any indexes in tables (?). 也许你正在搜索的列没有正确编入索引(!?),或者表中没有任何索引(?)。

  • Avoid .ToList(), this always creates a sql query when expanding or using related entities 避免.ToList(),这在扩展或使用相关实体时总是会创建一个sql查询
  • CompiledQueries compiles the queries in advance, avoiding some overhead ( not much) CompiledQueries提前编译查询,避免一些开销(不多)
  • Debug your queries ( eg. using http://miniprofiler.com/ ) 调试您的查询(例如,使用http://miniprofiler.com/

I would put my bet on a simple subquery, "naturally" representing the information needed, like this 我会把我的赌注放在一个简单的子查询上,“自然地”代表所需的信息,就像这样

var query =
    from r in context.StaffingResourceDatas
    where r.EmployeeId != null && !exclusionList.Contains(r.ResourceTitleId)
    select new ChartResourceModel
    {
        ResourceId = r.ResourceId,
        Division = r.ResourceDivision,
        Type = r.ResourceType,
        Dates = dates.Select(d => new ChartDateModel
        {
            Date = d,
            Available = context.StaffingForecasts.Where(f => 
                f.ResourceId == r.ResourceId && f.Date == d).Sum(f => f.Hours) < 24
        }).ToList()
    };
var sqlQuery = query.ToString();
var result = query.ToList();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM