【问题标题】:Slow LINQ Query on Join加入时的慢速 LINQ 查询
【发布时间】:2019-07-30 12:31:28
【问题描述】:

我正在使用带有 .NET Core 的 EF 6。我编写了以下查询以将一些数据选择到自定义对象中。目前主查询返回超过 25000 条记录。然后应用过滤器。此查询运行非常缓慢。我只是想找出我是否在这里犯了任何错误。为什么它会这么慢,并且过滤发生在内存而不是数据库端?

public IQueryable<TicketViewModel> GetTickets(int companyId, string locale, TicketFilterViewModel filters, Guid customerRef, int? take, int currentUser)
        {
            IQueryable<TicketViewModel> tickets = (from to in _context.Ticket
                                                   join co in _context.Company on to.CompanyId equals co.CompanyId
                                                   join con in _context.Contact on to.ContactId equals con.ContactId
                                                   join site in _context.Site on to.SiteId equals site.SiteId
                                                   join country in _context.Country on site.CountryId equals country.CountryId
                                                   join cus in _context.Customer on con.CustomerId equals cus.CustomerId
                                                   join tic_type in _context.TicketType on to.TicketTypeId equals tic_type.TicketTypeId
                                                   join ts in _context.TicketStatus on to.TicketStatusId equals ts.TicketStatusId
                                                   join sb in _context.ServiceBoard on to.ServiceBoardId equals sb.ServiceBoardId into ob1
                                                   from a in ob1.DefaultIfEmpty()
                                                   join u in _context.User on to.TechnitianId equals u.Id into ob2
                                                   from b in ob2.DefaultIfEmpty()
                                                   join pr in _context.Priority on to.PriorityId equals pr.PriorityId into ob3
                                                   from c in ob3.DefaultIfEmpty()                                                  
                                                   where to.CompanyId == companyId && (customerRef == Guid.Empty || cus.RefNo == customerRef)
                                                   && to.MergedIntoTicketId == null
                                                   select new TicketViewModel
                                                   {
                                                       CreatedOn = Helpers.Custom.UtcToStandardTime(locale, to.AddedOnUtc).ToString("dd/MM/yyyy hh:mm tt"),
                                                       CustomerName = cus.CustomerName,
                                                       TicketNumber = to.TicketNumber,
                                                       TicketTitle = to.TicketTitle,
                                                       RefNo = to.RefNo,
                                                       CompanyName = co.CompanyName,
                                                       ContactName = string.Concat(con.FirstName, " ", con.LastName),
                                                       SiteAddress = String.Concat(site.AddressLine1, ", ", site.AddressLine2, ", ", site.Suburb, ", ", site.State, ", ", site.PostCode, ", ", country.Name),
                                                       ServiceBoardName = a.BoardName,
                                                       TechnicianName = string.Concat(b.FirstName, " ", b.LastName),
                                                       PriorityName = c.PriorityName,
                                                       TicketStatus = ts.StatusName, //d.StatusName,
                                                       ServiceBoardId = to.ServiceBoardId,
                                                       TicketStatusId = to.TicketStatusId,
                                                       CustomerId = cus.CustomerId,
                                                       PriorityId = to.PriorityId,
                                                       ContractId = site.ContractId,
                                                       TechnitianId = to.TechnitianId,
                                                       TicketId = to.TicketId,
                                                       StatusCategoryId = ts.CategoryId,//,//d.CategoryId,
                                                       DueOnUtc = to.DueOnUtc,
                                                       DefaultStatusId = ts.DefaultId,//d.DefaultId,
                                                       ClosedOnUtc = to.ClosedOnUtc,
                                                       ResolvedOnUtc = to.ResolvedOnUtc,
                                                       InitialResponseMade = to.InitialResponseMade,
                                                       TicketTypeId = to.TicketTypeId,
                                                       TicketTypeName = tic_type.TicketTypeName

                                                   }).OrderByDescending(o => o.TicketNumber);


            bool isFiltersHit = false;

            if (filters != null)
            {
                if (tickets != null && tickets.Count() > 0 && filters.serviceboard_selectedItems != null && filters.serviceboard_selectedItems.Count > 0)
                {
                    isFiltersHit = true;

                    tickets = tickets.Where(x => x.ServiceBoardId != null && filters.serviceboard_selectedItems.Select(o => o.serviceBoardId).Contains(x.ServiceBoardId.Value));

                }

                if (tickets != null && tickets.Count() > 0 && filters.status_selectedItems != null && filters.status_selectedItems.Count > 0)
                {
                    isFiltersHit = true;

                    tickets = tickets.Where(x => x.TicketStatusId != null && filters.status_selectedItems.Select(o => o.ticketStatusId).Contains(x.TicketStatusId.Value));

                }

                if (tickets != null && tickets.Count() > 0 && filters.type_selectedItems != null && filters.type_selectedItems.Count > 0)
                {
                    isFiltersHit = true;

                    tickets = tickets.Where(x => filters.type_selectedItems.Select(o => o.ticketTypeId).Contains(x.TicketTypeId));

                }

                if (tickets != null && tickets.Count() > 0 && filters.technician_selectedItems != null && filters.technician_selectedItems.Count > 0)
                {
                    isFiltersHit = true;

                    tickets = tickets.Where(x => x.TechnitianId != null && filters.technician_selectedItems.Select(o => o.id).Contains(x.TechnitianId.Value));

                }

                if (tickets != null && tickets.Count() > 0 && filters.customer_selectedItems != null && filters.customer_selectedItems.Count > 0)
                {
                    isFiltersHit = true;

                    tickets = tickets.Where(x => x.CustomerId != 0 && filters.customer_selectedItems.Select(o => o.customerId).Contains(x.CustomerId));

                }

                if (tickets != null && tickets.Count() > 0 && filters.priority_selectedItems != null && filters.priority_selectedItems.Count > 0)
                {
                    isFiltersHit = true;

                    tickets = tickets.Where(x => x.PriorityId != null && filters.priority_selectedItems.Select(o => o.priorityId).Contains(x.PriorityId.Value));

                }

                if (tickets != null && tickets.Count() > 0 && filters.contract_selectedItems != null && filters.contract_selectedItems.Count > 0)
                {
                    isFiltersHit = true;

                    tickets = tickets.Where(x => x.ContractId != null && filters.contract_selectedItems.Select(o => o.contractId).Contains(x.ContractId.Value));

                }

                if (tickets != null && tickets.Count() > 0 && filters.source_selectedItems != null && filters.source_selectedItems.Count > 0)
                {
                    isFiltersHit = true;

                    tickets = tickets.Where(x => x.SourceId != 0 && filters.source_selectedItems.Select(o => o.item_id).Contains(x.SourceId));

                }

            }


            if (take.HasValue)
                return tickets.Take(take.Value);

            return tickets;
        }

【问题讨论】:

  • 你有没有试过看看生成的sql是什么样子的?
  • 是翻译后的 sql-query 还是你的代码执行速度慢?
  • 这不会改善查询,但尽可能使用导航属性将使代码更易于阅读。
  • 一个问题是当您执行Count 时,您会在那些if 语句中一遍又一遍地运行该查询。如果您没有任何结果,那么添加更多过滤器不会受到伤害。
  • 过滤器部分既不需要“tickets != null”也不需要“tickets.Count() > 0”(tickets 永远不会为空,你不在乎是否有任何结果因为当您尝试同时评估已完成的查询和过滤器时,惰性评估将构建实际的查询)。尝试将生成的查询字符串输入数据库查询优化器以查找是否缺少所需的索引。

标签: c# entity-framework linq .net-core


【解决方案1】:

如果您根据过滤器中的条件编写查询,您会看到很大的改进。意思是,创建一个基本查询(基本和最小条件),然后根据您的过滤器一一添加条件。检查所有过滤器后执行查询。

P.S:您实际上不需要在每个过滤器上都调用 tickets.Count()。一开始就做一次。

【讨论】:

  • 我不确定他们是否需要致电 Count。向不返回结果的查询添加更多过滤器仍然不会产生任何结果。
  • 谢谢,实际上是计数减慢了查询速度。我删除了它,现在它相当快。谢谢
  • 问题是,您甚至不需要Count,因为您实际上并没有在任何地方使用它。你可以试试Any()
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多