什么更快?结构体数组还是DataTable?

5
我正在使用LinqToSQL从SQL Server处理数据,将其转储到iSeries服务器进行进一步处理。关于这方面的更多细节,请点击此处
我的问题是处理这350行数据需要大约1.25分钟。我仍在尝试解密来自SQL Server Profiler的结果,但运行了大量查询。以下是我正在做的一些详细信息:
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。


我的看法是,数据表在本十年的代码中根本没有任何用处。 - Chris Marisic
1个回答

7
您的代码中有几个需要改进的地方:
  1. 您在 'var vehicles' 查询中为每个项目查询数据库多次,您可以重写该查询以减少数据库查询次数。
  2. 当您不需要查询实体的所有属性或需要该实体的子实体时,最好使用匿名类型来进行 select。LINQ to SQL 会分析这一点并从数据库检索较少的数据。这样的选择可能看起来像这样:select new { a.VehicleId, a.Name }
  3. GetPartsAndOilCosts 中的查询可以通过将计算 cost.totalCost * (1 + OVERHEAD_RATE) 放入 LINQ 查询中进行优化。这样查询就可以完全在数据库中执行,应该会快得多。
  4. 您正在对 var vehicles 查询执行 Count(),但您仅将其用于确定数组大小。虽然 LINQ to SQL 会对其进行非常有效的 SELECT count(*) 查询,但它需要额外的往返数据库。此外(根据隔离级别),您开始迭代查询项目的时间可能会添加一个项目。在这种情况下,您的数组太小,将引发 ArrayIndexOutOfBoundsException。您可以简单地在查询上使用 .ToArray() 或创建一个 List<EquipmentTable> 并在其中调用 .ToArray()。这通常足够快,特别是当您只有 380 个项目时,而且肯定比多出一个往返数据库(计数)要快。
  5. 正如您可能已经预料到的那样,实际问题在于数据库查询的数量。在结构数组或 DataTable 之间切换不会产生太大的差异。
  6. 在优化了尽可能多的查询之后,开始分析剩余的查询(使用 SQL 分析器),并使用索引调整向导优化这些查询。它将为您提供一些新的索引,这可能会显着加快速度。

对于第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上再次询问,但请记住将问题简化到最小的可能性,因为阅读某人的混乱并不好玩(我们没有为此付费):-)祝你好运。

@Steven:抱歉,我对第一点感到困惑。我不确定该如何做。其他所有的点我都在研究中。 - Mike Wills

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接