我使用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++;
}
}
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
我的想法是减少对DB的查询数量,以加快处理速度.如果LINQ为每条记录执行SELECT,则可能需要先将每条记录加载到内存中.
我仍然认为自己是C#和OOP的初学者(我主要在iSeries上进行RPG编程).所以我猜我做的事情很愚蠢.你能帮助我解决我的愚蠢问题(至少在这个问题上)吗?
更新:我想我会回来告诉你我发现了什么.看起来数据库的设计很差.无论LINQ在后台生成什么,它都是非常低效的代码.我不是说LINQ不好,这对这个数据库来说不好.我转换为快速抛出.XSD设置,处理时间从1.25分钟到15秒.一旦我进行了适当的重新设计,我只能猜测我会再缩短几秒钟.谢谢大家的评论.我会在更好的数据库上再次尝试LINQ.
我在您的代码中发现了一些内容:
select
.LINQ to SQL将对此进行分析并从数据库中检索较少的数据.这样的选择可能如下所示:select new { a.VehicleId, a.Name }
GetPartsAndOilCosts
可以通过将计算进行优化,cost.totalCost * (1 + OVERHEAD_RATE)
在LINQ查询.这样查询就可以在数据库中完全执行,这样可以使查询更快.Count()
的var vehicles
查询,但你只是用它来确定数组的大小.虽然LINQ to SQL将对它进行非常有效的SELECT count(*)
查询,但它需要额外的数据库往返.除此之外(取决于您的隔离级别)您开始迭代查询的时间可以添加项目.在这种情况下,您的数组太小而且ArrayIndexOutOfBoundsException
会被抛出.您可以简单地使用.ToArray()
查询或创建List<EquipmentTable>
并调用.ToArray()
它.这通常足够快,特别是当你在这个集合中只有380个项目时,它肯定会比对数据库进行额外的往返更快(计数).点#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.
}
}
Run Code Online (Sandbox Code Playgroud)
您应该尝试完成的是删除query2
子查询,因为它在顶部查询的每一行上执行.所以你最终会得到这样的东西:
var query =
from x in A
from y in B
where x.Value == y.Value
select something;
foreach (var row in query)
{
}
Run Code Online (Sandbox Code Playgroud)
当然这个例子很简单,在现实生活中它变得非常复杂(正如你已经注意到的那样).在你的情况下,因为你有多个'子查询'.这可能需要一些时间才能做到这一点,特别是在您缺乏LINQ to SQL知识的情况下(正如您自己所说).
如果你无法弄明白,你可以随时在Stackoverflow再次询问,但请记住将问题排除在最小的可能之外,因为阅读某人的混乱并不是一件好事(我们没有得到报酬) :-) 祝好运.