父子表的SQL策略

时间:2010-08-27 20:38:18

标签: c# sql linq query-optimization

我可以使用一些帮助来确定从SQL db中检索父子对象的最佳(最高性能/易于维护)策略。

我继承了这段代码,而且我的截止日期相对较短,我希望尽可能少地进行基础更改。我将有足够的时间用下一个螺旋来实现nhibernate或其他ORM,我现在不能这样做。我正在寻找最好的事情,在最短的时间内完成最少的修改。

扭曲是有不同的子类型(实现一个共同的子接口)。

例如,
父级:VehicleFleet(包含车队名称,经理名称,车辆清单)
儿童:IVehicle(包含品牌,型号,位置等)

然而,可能存在多种类型的IVehicle - 例如Car,Van,Motorcycle - 每种都具有不同的属性/列。汽车,厢式车和摩托车有一个单独的桌子。可能有也可能没有VehicleBase表,其中包含适用于任何IVehicle的列。

返回多个VehicleFleet对象的最佳策略是什么,每个对象都有相关的车辆孩子?

这是我尝试过的一些策略(以伪代码表示) -

假设:
所有GetXXXX函数都在幕后使用DataReader

方法1:简单&慢 - 这是最糟糕的做法,原因很明显

IEnumerable<Fleet> GetFleetsAndVehicles () {
 foreach (var fleet in myFleetDao.GetAllFleets ()) {
  foreach (var vehicleTypeDao in myVehicleTypeDaos)
   fleet.Vehicles.Add (vehicleTypeDao.GetVehicles (fleet.Id);
  yield return fleet;
 }
 yield break;
}

方法2:预取孩子

IEnumerable<Fleet> GetFleetsAndVehicles () {
 var allVehicles = (from vtd in myVehicleTypeDaos
    from v in vtd.GetAllVehicles()
    select v).ToLookup (v => v.FleetId);

 foreach (var fleet in myFleetDao.GetAllFleets ())
 {
  fleet.Vehicles = allVehicles[fleet.Id].ToList ();
  yield return fleet;
 }
 yield break;
}

方法3:预取子项,异步附加子项

IEnumerable<Fleet> GetFleetsAndVehicles () {
 foreach (var fleet in new AsyncGetter.GetFleetsAndVehicles ())
  yield return fleet;
 yield break;
}

class AsyncGetter
{
 // left out instance variables, Auto/Manual Reset Events, locking, etc. for brevity
 IEnumerable<Fleet> GetFleetsAndVehicles ()
 {
  StartAsyncStuff ();

  while (myUnconsumedFleets.Count > 0)
  {
   yield return myUnconsumedFleets.Remove (0);
   WaitUntilMoreFleetsAreAdded ();
  }
  yield break;
 }

 void StartAsyncStuff ()
 {
  myAllVehicles = <same as method 2>

  foreach (var fleet in myFleetDao.GetAllFleets ())
  {
   AttachVehiclesAsync (fleet);
  }
 }

 void AttachVehiclesAsync (Fleet f)
 {
  // assume using ThreadPool.QueueUserWorkItem right now
  WaitForAllVehiclesToLoad ();
  f.Vehicles = myAllVehicles[f.Id].ToList ();
  myUnconsumedFleets.Add (f);
 }
}

方法4:交错父/子查询

IEnumerable<Fleet> GetFleetsAndVehicles () {
 var allVehicles = from vtd in myVehicleTypeDaos
    from v in vtd.GetAllVehicles()
    orderby v.FleetId
    select v;
 var allVehiclesEnumerator = allVehicles.GetEnumerator ();

 foreach (var fleet in myFleetDao.GetAllFleets ())
 {
  fleet.Vehicles = GetAllChildVehiclesAndMaintainEnumeratorPosition (allVehiclesEnumerator, fleet);
  yield return fleet;
 }
}

到目前为止,使用一些测试数据,我发现方法3的性能最高(比最好的方法快27%),而方法1是最差的(比方法1慢4倍)。

所以,如果你有建议,我很乐意听到它们!

2 个答案:

答案 0 :(得分:0)

由于没有为手头的问题提供有用的答案而被贬低的风险,我仍然需要说:I thought we had left the 'DIY data access layers' behind these days。当然,可能仍有一些用例,你真的需要做一些自定义数据阅读器魔术。将继承层次结构从数据库映射到对象模型通常不是其中之一。

有很多ORM可以解决这个问题。它被称为“每种类型的表继承映射”。任何体面的ORM都支持这一点,并允许您急切地获取父/子关系。

如果性能真的是一个问题(是吗?),那么你可能会通过切换到“单表继承”策略(一个表中的所有类型,带有鉴别器列)来获得最大收益。 / p>

Entity Framework和NHibernate都支持开箱即用的单表和每表类型。 Linq 2 SQL(好的,可能不是完整的ORM)只支持单表继承;正如@Albin Sunnanbo所说,如果你可以改变数据库模式,它可能是一个选项。还有很多其他的ORM值得调查。

在那里,它不在我的胸前;-),希望它有所帮助。

答案 1 :(得分:0)

我认为如果您首先获得所有需要选择的车队的所有车辆,并且在获得该结果时填充车队对象,那么它将是最快的。

现在你肯定已经解决了,哪一个是最好的?