代码之家  ›  专栏  ›  技术社区  ›  Mike Wills

什么更快?结构数组或数据表

  •  5
  • Mike Wills  · 技术社区  · 14 年前

    我正在使用linqtosql处理来自sql server的数据,将其转储到iSeries服务器以进行进一步处理。 More details on that here .

    我的问题是,处理这350行数据大约需要1.25分钟。我仍在试图破译来自sql server分析器的结果,但仍有大量的查询正在运行。以下是关于我正在做什么的更多细节:

    using (CarteGraphDataDataContext db = new CarteGraphDataDataContext())
    {
        var vehicles = from a in db.EquipmentMainGenerals
                       join b in db.EquipmentMainConditions on a.wdEquipmentMainGeneralOID equals b.wdEquipmentMainGeneralOID
                       where b.Retired == null
                       orderby a.VehicleId
                       select a;
    
        et = new EquipmentTable[vehicles.Count()];
    
        foreach (var vehicle in vehicles)
        {
           // Move data to the array
    
           // Rates
           GetVehcileRates(vehicle.wdEquipmentMainGeneralOID);
    
           // Build the costs accumulators
           GetPartsAndOilCosts(vehicle.VehicleId);
           GetAccidentAndOutRepairCosts(vehicle.wdEquipmentMainGeneralOID);
    
           // Last Month's Accumulators
           et[i].lastMonthActualGasOil = GetFuel(vehicle.wdEquipmentMainGeneralOID) + Convert.ToDecimal(oilCost);
           et[i].lastMonthActualParts = Convert.ToDecimal(partsCost);
           et[i].lastMonthActualLabor = GetLabor(vehicle.VehicleId);
           et[i].lastMonthActualOutRepairs = Convert.ToDecimal(outRepairCosts);
           et[i].lastMonthActualAccidentCosts = Convert.ToDecimal(accidentCosts);
    
           // Move more data to the array
    
           i++;
       }
    }
    

    get方法看起来都类似于:

    private void GetPartsAndOilCosts(string vehicleKey)
    {
       oilCost = 0;
       partsCost = 0;
    
       using (CarteGraphDataDataContext db = new CarteGraphDataDataContext())
       {
          try
          {
             var costs = from a in db.WorkOrders
                         join b in db.MaterialLogs on a.WorkOrderId equals b.WorkOrder
                         join c in db.Materials on b.wdMaterialMainGeneralOID equals c.wdMaterialMainGeneralOID
                         where (monthBeginDate.Date <= a.WOClosedDate && a.WOClosedDate <= monthEndDate.Date) && a.EquipmentID == vehicleKey
                         group b by c.Fuel into d
                         select new
                                {
                                    isFuel = d.Key,
                                    totalCost = d.Sum(b => b.Cost)
                                };
    
              foreach (var cost in costs)
              {
                 if (cost.isFuel == 1)
                 {
                    oilCost = (double)cost.totalCost * (1 + OVERHEAD_RATE);
                 }
                 else
                 {
                    partsCost = (double)cost.totalCost * (1 + OVERHEAD_RATE);
                 }
              }
           }
           catch (InvalidOperationException e)
           {
              oilCost = 0;
              partsCost = 0;
           }
        }
    
        return;
     }
    

    我的想法是减少对数据库的查询数量,这样可以加快处理速度。如果linq为每条记录做了一个select,那么我可能需要首先将每条记录加载到内存中。

    我仍然认为自己是一个C和OOP的初学者(我主要在iSeries上做RPG编程)。所以我猜我在做蠢事。你能帮我纠正我的愚蠢吗(至少在这个问题上)?

    更新: 我想我会回来告诉你我发现了什么。看起来数据库设计得很差。无论linq在后台生成什么,它都是非常低效的代码。我不是说LINQ不好,只是对这个数据库不好。我转换成了一个快速拼凑的.xsd设置,处理时间从1.25分钟变为15秒。一旦我做了一个适当的重新设计,我只能猜测我会剃掉更多的几秒钟。谢谢大家的评论。改天我会在一个更好的数据库里再试试linq。

    1 回复  |  直到 14 年前
        1
  •  7
  •   Steven    14 年前

    我在你的代码中发现了一些东西:

    1. 对于“var vehicles”查询中的每一项,您都要多次查询数据库,您可能需要重写该查询,以便减少所需的数据库查询。
    2. 当您不需要查询实体的所有属性或该实体的子实体时,在 select . linq to sql将对此进行分析,并从数据库中检索较少的数据。这样的选择可能如下所示: select new { a.VehicleId, a.Name }
    3. 中的查询 GetPartsAndOilCosts 可以通过计算来优化 cost.totalCost * (1 + OVERHEAD_RATE) 在linq查询中。这样查询就可以在数据库中完全执行,这将使查询速度更快。
    4. 你在做 Count() var vehicles 查询,但仅用于确定数组的大小。而linq to sql将使 SELECT count(*) 查询它,需要额外的数据库往返。除此之外(取决于您的隔离级别),您开始迭代查询的时间还可以添加一个项。在这种情况下,数组太小, ArrayIndexOutOfBoundsException 会被扔掉。你可以简单地使用 .ToArray() 在查询或创建 List<EquipmentTable> 并打电话 toRayay.() 就这点而言。这通常会足够快,特别是当您在这个集合中只有380个项目时,它肯定会比有一个额外的数据库往返(计数)更快。
    5. 正如您可能已经预料到的,数据库查询的数量是真正的问题。在struct数组或datatable之间切换不会有太大的不同。
    6. 优化完尽可能多的查询后,开始分析剩余的查询(使用sql profiler),并使用索引优化向导优化这些查询。它将为您提出一些新的索引,这可能会大大加快速度。

    对第1点有一点额外的解释。你在这里做的有点像这样:

    var query = from x in A select something;
    
    foreach (var row in query)
    {
        var query2 = from y in data where y.Value = row.Value select something;
    
        foreach (var row2 in query2)
        {
            // do some computation.
        }
    }
    

    你应该努力做到的是去掉 query2 子查询,因为它正在顶部查询的每一行上执行。所以你可能会得到这样的结果:

    var query =
        from x in A
        from y in B
        where x.Value == y.Value
        select something;
    
    foreach (var row in query)
    {
    }
    

    当然这个例子很简单,在现实生活中会变得非常复杂(正如你已经注意到的)。在您的情况下,还因为您有多个这些“子查询”。您可能需要一些时间才能正确地理解这一点,特别是由于您缺乏linq to sql的知识(正如您自己所说)。

    如果你想不出来,你可以在StackOverflow上再问一次,但是请记住把你的问题分解到尽可能小的范围,因为读别人的烂摊子是没有乐趣的(我们不会为此得到报酬):-) 祝你好运。