【问题标题】:c# linq syntax slow due to multiple queries in single queryc# linq 语法由于单个查询中的多个查询而变慢
【发布时间】:2014-01-16 23:16:30
【问题描述】:

我想知道是否有更好、更有效的方法来重新编码下面的 linq 语法以使查询运行得更快,即只需调用一次数据库。我的数据库位于远程,这导致速度很慢:

    var query = (from ticket in dataClassesDataContext.Tickets.Where(TicketsToShow.And(SearchVals))
                                select new 
                                {
                                    Priority = ticket.TicketPriority.TicketPriorityName,
                                    Ticket = string.Format(TicketFormat, ticket.TicketID),
                                    AssetId = ticket.Asset.Serial,
                                    OpenDate = ticket.CheckedInDate,
                                    OpenFor = CalculateOpenDaysAndHours(ticket.CheckedInDate, ticket.ClosedDate),
                                    Account = ticket.Account.Customer.Name,
                                    Description = ticket.Description.Replace("\n", ", "),
                                    Status = ticket.TicketStatus.TicketStatusName,
                                    Closed = ticket.ClosedDate,
  THIS IS THE CAUSE ====>>>         Amount = GetOutstandingBalanceForTicket(ticket.TicketID),
                                    Paid = ticket.Paid,
                                    Warranty = ticket.WarrantyRepair,
                                    AssetLocation = GetAssetLocationNameFromID(ticket.Asset.LocationID, AssLocNames)
                                }).Skip(totalToDisplay * page).Take(totalToDisplay);

                    if (SortOrder.ToLower().Contains("Asc".ToLower()))
                    {
                        query = query.OrderBy(p => p.OpenDate);
                    }
                    else
                    {
                        query = query.OrderByDescending(p => p.OpenDate);
                    }//ENDIF

性能不佳的主要原因是下面的函数 GetOutstandingBalanceForTicket 中的代码,该代码计算发票中所有项目的总和并将其以字符串形式返回:

public static string GetOutstandingBalanceForTicket(int TicketID)
{
    string result = string.Empty;
    decimal total = 0;

    try
    {
        using (DataClassesDataContext dataClassesDataContext = new DataClassesDataContext(cDbConnection.GetConnectionString()))
        {
            var queryCustomerTickets = from ticket in dataClassesDataContext.Tickets
                                       where
                                       (ticket.TicketID == TicketID)
                                       select ticket;

            if (queryCustomerTickets != null)
            {
                foreach (var ticket in queryCustomerTickets)
                {
                    var queryTicketChargeItems = from chargeItem in dataClassesDataContext.ProductChargeItems
                                                 where chargeItem.ChargeID == ticket.ChargeID &&
                                                 chargeItem.Deleted == null
                                                 select chargeItem;

                    foreach (var chargeItem in queryTicketChargeItems)
                    {
                        total += (chargeItem.Qty * chargeItem.Price);
                    }
                }
            }
        }
    }
    catch (Exception ex)
    {

    }

    return total.ToString("0.##");
}

提前谢谢你。

【问题讨论】:

  • 这可能是您想要推迟到数据库的那些事情之一。也就是说,一个函数或存储过程计算总数并将其存储在其他地方。您甚至可以随时保持运行总计。
  • 为什么Amount 是一个字符串?
  • 是的,GetOutstandingBalanceForTicket(..) 返回的字符串

标签: c# sql linq


【解决方案1】:

正如您所指出的,这段代码非常慢,因为每张票都需要查询。

为了消除多次查询的需要,您应该考虑在ticketsToShow 和tickets 实体(在ticketid 上)之间应用内连接,使用groupby 来提供每张门票的费用总和。

LINQ: Using INNER JOIN, Group and SUM的答案很好地说明了这一点

【讨论】:

    【解决方案2】:

    理想情况下,您可能会将其更多地视为一次性加载所有类型的设置。但是,我不认为 linq2sql 支持(我知道 EF 支持)。您可以做的一件事是避免嵌套查询。由于您已经可以访问票证表,也许您应该从您的select 语句中对其发出Sum()。我很难验证这是否是一种改进,所以如果你愿意的话,这段代码是即时的。

     //(from ticket in dataClassesDataContext.Tickets.Where(TicketsToShow.And(SearchVals))
     (from ticket in dataClassesDataContext.Tickets
    
    //this would be where you could eager load if possible (not entirely required)
    //.Include is an EF method used only as example
    /*.Include(t => t.TicketPriority)//eager load required entities
     .Include(t => t.Asset)//eager load required entities
     .Include(t => t.Account.Customer)//eager load required entities
     .Include(t => t.TicketStatus)//eager load required entities
     .Include(t => t.ProductChargeItems)//eager load required entities
    */
    
     .Where(TicketsToShow.And(SearchVals))
       select new 
       {
           Priority = ticket.TicketPriority.TicketPriorityName,
           Ticket = string.Format(TicketFormat, ticket.TicketID),
           AssetId = ticket.Asset.Serial,
           OpenDate = ticket.CheckedInDate,
           OpenFor = CalculateOpenDaysAndHours(ticket.CheckedInDate, ticket.ClosedDate),
           Account = ticket.Account.Customer.Name,
           Description = ticket.Description.Replace("\n", ", "),
           Status = ticket.TicketStatus.TicketStatusName,
           Closed = ticket.ClosedDate,
           //Use Sum and the foreign relation instead of a nested query
           Amount = ticket.ProductChargeItems.Where(pci => pci.Deleted == null).Sum(pci => pci.Qty * pci.Price),
           Paid = ticket.Paid,
           Warranty = ticket.WarrantyRepair,
           AssetLocation = GetAssetLocationNameFromID(ticket.Asset.LocationID, AssLocNames)
       }).Skip(totalToDisplay * page).Take(totalToDisplay);
    
       if (SortOrder.ToLower().Contains("Asc".ToLower()))
       {
           query = query.OrderBy(p => p.OpenDate);
       }
       else
       {
           query = query.OrderByDescending(p => p.OpenDate);
       }
    

    【讨论】:

    • 您在哪里重新编码了该行:“Amount = ticket.ProductChargeItems.Where(pci => pci.Deleted == null).Sum(pci => pci.Qty * pci.Price)” productchargeitems 来自不同的数据环境。我应该做类似“(来自 dataClassesDataContext.ProductChargeItems.Where(pci => pci.Deleted == null).Sum(pci => pci.Qty * pci.Price)) 中的chargeItem,而不是(尽管这可能不会按照我认为语法错误的方式工作!
    • 这将是在此实例中使用的连接,根据上面 Alan Wolman 对 Join Tickets 和 ProductChargeItems 的回答吗?
    【解决方案3】:

    我认为,您可以使这个查询更简单。有这样的想法:

    public static string GetOutstandingBalanceForTicket(DataClassesDataContext context, int TicketID)
    {
        decimal total = 0;
    
        var total = (from ticket in context.Tickets
                     join chargeItem from context.ProductChargeItems on chargeItem.ChargeID == ticket.ChargeID
                     where (ticket.TicketID == TicketID && chargeItem.Deleted == null)
                     select chargeItem).Sum(chargeItem => chargeItem.Qty * chargeItem.Price);
    
    
        return total.ToString("0.##");
    }
    /*...*/
    Amount = GetOutstandingBalanceForTicket(dataClassesDataContext, ticket.TicketID),
    

    现在,您可以在查询中内联此方法。

    它可能包含语法错误,因为我是用记事本写的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多