1

这是一个关于速度的问题 - 有很多记录要访问。

问题基本信息

例如,我们将在数据库中拥有三个表。

关系:Order-ProductInOrder 是一对多(一个订单可以有多个产品) ProductInOrder-产品是一对一(订单中的一个产品由一个产品表示)

public class Order {
  public bool Processed { get; set; }
  // this determines whether the order has been processed
  // - orders that have do not go through this again
  public int OrderID { get; set; } //PK
  public decimal TotalCost{ get; set; }
  public List<ProductInOrder> ProductsInOrder;
  // from one-to-many relationship with ProductInOrder
  // the rest is irrelevant and will not be included here
}
//represents an product in an order - an order can have many products
public class ProductInOrder {
  public int PIOD { get; set; } //PK
  public int Quantity{ get; set; }
  public int OrderID { get; set; }//FK
  public Order TheOrder { get; set; }
  // from one-to-many relationship with Order
  public int ProductID { get; set; } //FK
  public Product TheProduct{ get; set; }
  //from one-to-one relationship with Product
}
//information about a product goes here
public class Product {
  public int ProductID { get; set; } //PK
  public decimal UnitPrice { get; set; } //the cost per item
  // the rest is irrelevant to this question
}

假设我们收到一批订单,我们需要对其应用折扣并找到订单的总价。这可能适用于从 10,000 到超过 100,000 个订单的任何地方。其运作方式是,如果订单有 5 个或更多产品,每个产品的成本为 100 美元,我们将为总价提供 10% 的折扣。

我试过的

我尝试了以下方法:

//this part gets the product in order with over 5 items
List<Order> discountedOrders = orderRepo
  .Where(p => p.Processed == false)
  .ToList();
List<ProductInOrder> discountedProducts = discountedOrders
  .SelectMany(p => p.ProductsInOrder)
  .Where(q => q.Quantity >=5 )
  .ToList();
discountedProducts = discountedProducts
  .Where(p => p.Product.UnitPrice >= 100.00)
  .ToList();
discountOrders = discountedOrders
  .Where(p => discountProducts.Any(q => q.OrderID == p.OrderID))
  .ToList();

这非常慢并且需要永远运行,当我在它上面运行集成测试时,测试似乎超时。我想知道是否有更快的方法来做到这一点。

4

5 回答 5

2

尽量不要ToList在每次查询后调用。

当您调用ToList查询时,它会被执行,并且对象会从内存中的数据库加载。基于第一个查询结果的任何后续查询都在列表的内存中执行,而不是直接在数据库中执行。您在这里要做的是在数据库上执行整个查询并仅返回那些验证您所有条件的结果。

var discountedOrders = orderRepo
  .Where(p=>p.Processed == false);
var discountedProducts = discountedOrders
  .SelectMany(p=>p.ProductsInOrder)
  .Where(q=>q.Quantity >=5);
discountedProducts = discountedProducts
  .Where(p=>p.Product.UnitPrice >= 100.00);
discountOrders = discountedOrders
  .Where(p=>discountProducts.Any(q=>q.OrderID == p.OrderID));
于 2012-09-06T14:24:37.473 回答
1

好吧,一方面,结合这些调用会加快一些速度。试试这个:

discountOrders =  orderRepo.Where(p=>p.Processed == false && p.SelectMany(q=>q.ProductsInOrder).Where(r=>r.Quantity >=5 && r.Product.UnitPrice >= 100.00 && r.OrderID == p.OrderId).Count() > 0).ToList();

请注意,这未经测试。我希望我的逻辑是正确的——我想我做到了,但如果我没有,请告诉我。

于 2012-09-06T14:32:24.023 回答
1

与@PhillipSchmidt 类似,您可以合理化您的 Linq

var discountEligibleOrders =
 allOrders
   .Where(order => !order.Processed 
                   && order
                    .ProductsInOrder
                    .Any(pio => pio.TheProduct.UnitPrice >= 100M 
                                && pio.Quantity >= 5))

删除所有这些讨厌的ToList语句是一个很好的开始,因为您从数据库中提取的集合可能比您需要的要大得多。让数据库完成工作。

要获取每个订单及其价格(假设折扣价为 0.9*标价):

var ordersAndPrices =
 allOrders
   .Where(order => !order.Processed)
   .Select(order => new {
                     order, 
                     isDiscounted = order
                       .ProductsInOrder
                       .Any(pio => pio.TheProduct.UnitPrice >= 100M 
                                   && pio.Quantity >= 5)
                    })
   .Select(x => new {
                  order = x.order, 
                  price = x.order
                           .ProductsInOrder
                           .Sum(p=> p.Quantity 
                                    * p.TheProduct.UnitPrice
                                    * (x.isDiscounted ? 0.9M : 1M))});
于 2012-09-06T14:38:11.377 回答
0

我知道你有一个可接受的答案,但请尝试这个以提高速度 - PLINQ(并行 LINQ)这将获取 4000 个列表,如果你有 4 个内核,它将在每个内核上过滤 1000 个,然后整理结果。

  List<Order> orders = new List<Order>();
  var parallelQuery = (from o in orders.AsParallel()
                       where !o.Processed
                       select o.ProductsInOrder.Where(x => x.Quantity >= 5 &&
                                                           x.TheProduct.UnitPrice >= 100.00 && 
                                                           orders.Any(x => x.OrderID = x.OrderID));

请看这里:

在许多情况下,PLINQ 可以通过更有效地使用主机上的所有可用内核来显着提高 LINQ to Objects 查询的速度。这种提高的性能为桌面带来了高性能计算能力

http://msdn.microsoft.com/en-us/library/dd460688.aspx

于 2012-09-06T14:58:56.867 回答
-1

将其移至 1 个查询中,但实际上您应该将其移至 SSIS 包或 sql 作业中。您可以轻松地将其设置为在不到一秒的时间内运行的存储过程。

于 2012-09-06T14:29:16.870 回答