1

FNH和NH的新功能。通過LEFT OUTER JOIN進行的大型一對多選擇查詢優化

我想優化OneToMany映射的查詢。我實際上只希望檢索Segment = 0中的一個。PlantID,AreaID,CellID,DeviceID,StartDateTime組成了Data的複合主鍵。將分段添加到DataMore1和DataMore2的組合鍵。

一個事件可以記錄多達400個數據點...取決於他們選擇的選項。 因此,我們選擇將數據存儲在單獨的表格中,以便在x天或數月後清除不需要的數據。

目標是通過FluentNHibernate做這樣的事情。

SELECT  Data.PlantID, Data.AreaID, Data.CellID, Data.DeviceID, Data.StartDateTime, Data.DataPoint01, DataMore1.Segment, DataMore1.DataPoint101, 
         DataMore2.DataPoint201 
FROM   Data 
LEFT OUTER JOIN 
         DataMore1 ON Data.PlantID = DataMore1.PlantID AND Data.AreaID = DataMore1.AreaID AND Data.CellID = DataMore1.CellID AND 
         Data.DeviceID = DataMore1.DeviceID AND Data.StartDateTime = DataMore1.StartDateTime AND **DataMore1.Segment = 0** 
LEFT OUTER JOIN 
         DataMore2 ON Data.PlantID = DataMore2.PlantID AND Data.AreaID = DataMore2.AreaID AND Data.CellID = DataMore2.CellID AND 
         Data.DeviceID = DataMore2.DeviceID AND Data.StartDateTime = DataMore2.StartDateTime AND **DataMore2.Segment = 0** 

模型

public class Data 
{ 
     public virtual int PlantID { get; set; } 
     public virtual int AreaID { get; set; } 
     public virtual int CellID { get; set; } 
     public virtual int DeviceID { get; set; } 
     public virtual DateTime StartDateTime { get; set; } 
     public virtual float DataPoint01 { get; set; } 
     public virtual float DataPoint02 { get; set; } 
     public virtual float DataPoint99 { get; set; } 
     public virtual IEnumerable<DataMore1> DataMore1 { get; set; } 
     public virtual IEnumerable<DataMore2> DataMore2 { get; set; } 
} 

public class DataMore1 
{ 
     public virtual int PlantID { get; set; } 
     public virtual int AreaID { get; set; } 
     public virtual int CellID { get; set; } 
     public virtual int DeviceID { get; set; } 
     public virtual DateTime StartDateTime { get; set; } 
     public virtual byte Segment { get; set; } 
     public virtual float DataPoint101 { get; set; } 
     public virtual float DataPoint102 { get; set; } 
     public virtual float DataPoint199 { get; set;} 
} 

public class DataMore2 
{ 
     public virtual int PlantID { get; set; } 
     public virtual int AreaID { get; set; } 
     public virtual int CellID { get; set; } 
     public virtual int DeviceID { get; set; } 
     public virtual DateTime StartDateTime { get; set; } 
     public virtual byte Segment { get; set; } 
     public virtual float DataPoint201 { get; set; } 
     public virtual float DataPoint202 { get; set; } 
     public virtual float DataPoint299 { get; set; } 
} 

而映射

public DataMap() 
{ 
    Table("Data"); 
     CompositeId() 
    .KeyProperty(e => e.PlantID) 
    .KeyProperty(e => e.AreaID) 
    .KeyProperty(e => e.CellID) 
    .KeyProperty(e => e.DeviceID) 
      .KeyProperty(e => e.StartDateTime); 
     Map(e => e.DataPoint01); 
     Map(e => e.DataPoint02); 
     Map(e => e.DataPoint99); 
     HasMany(e => e.DataMore1) 
      .KeyColumns.Add("PlantID") 
      .KeyColumns.Add("AreaID") 
      .KeyColumns.Add("CellID") 
      .KeyColumns.Add("DeviceID") 
      .KeyColumns.Add("StartDateTime") 
      .Cascade.All().Inverse(); 
     HasMany(e => e.DataMore2) 
      .KeyColumns.Add("PlantID") 
      .KeyColumns.Add("AreaID") 
      .KeyColumns.Add("CellID") 
      .KeyColumns.Add("DeviceID") 
      .KeyColumns.Add("StartDateTime") 
      .Cascade.All().Inverse(); 
} 

public DataMore1Map() 
{ 
    Table("DataMore1"); 
    CompositeId() 
     .KeyProperty(e => e.PlantID) 
     .KeyProperty(e => e.AreaID) 
     .KeyProperty(e => e.CellID) 
     .KeyProperty(e => e.DeviceID) 
     .KeyProperty(e => e.StartDateTime) 
     .KeyProperty(e => e.Segment); 
    Map(e => e.DataPoint101); 
    Map(e => e.DataPoint102); 
    Map(e => e.DataPoint199); 
} 

public DataMore2Map() 
{ 
    Table("DataMore2"); 
    CompositeId() 
     .KeyProperty(e => e.PlantID) 
     .KeyProperty(e => e.AreaID) 
     .KeyProperty(e => e.CellID) 
     .KeyProperty(e => e.DeviceID) 
     .KeyProperty(e => e.StartDateTime) 
     .KeyProperty(e => e.Segment); 
    Map(e => e.DataPoint201); 
    Map(e => e.DataPoint202); 
    Map(e => e.DataPoint299); 
} 

回答

1

所以,這是一個查詢的問題?

由於您是NH的新手,所以我給出了比所需的更廣泛的答案。前兩門課應該自己回答你的問題。

public class DataQuery : AbstractQueryObject<IList<DataQueryResult>> 
{ 
    public override IList<DataQueryResult> GetResult() 
    { 
     DataMore1 dm1 = null; 
     DataMore2 dm2 = null; 

     var list = 
      this.Session.QueryOver<Data>() 
       .JoinAlias(data => data.DataMore1,() => dm1, NHibernate.SqlCommand.JoinType.LeftOuterJoin, Expression.Eq("Segment", 0)) 
       .JoinAlias(data => data.DataMore2,() => dm2, NHibernate.SqlCommand.JoinType.LeftOuterJoin, Expression.Eq("Segment", 0)) 
       .Select(p => 
        new DataQueryResult 
        { 
         PlantID = p.PlantID, 
         AreaID = p.AreaID, 
         CellID = p.CellID, 
         DeviceID = p.DeviceID, 
         StartDateTime = p.StartDateTime, 
         DataPoint01 = p.DataPoint01, 
         DataMore1Segment = dm1.Segment, 
         DataMore1DataPoint101 = dm1.DataPoint101, 
         DataMore2Segment = dm2.Segment, 
         DataMore2DataPoint201 = dm2.DataPoint201 
        }) 
       .List<DataQueryResult>(); 

     return list; 
    } 
} 

public class DataQueryResult 
{ 
    internal DataQueryResult() { } 

    public int PlantID { get; internal set; } 
    public int AreaID { get; internal set; } 
    public int CellID { get; internal set; } 
    public int DeviceID { get; internal set; } 
    public DateTime StartDateTime { get; internal set; } 
    public float DataPoint01 { get; internal set; } 
    public byte? DataMore1Segment { get; internal set; } 
    public float? DataMore1DataPoint101 { get; internal set; } 
    public byte? DataMore2Segment { get; internal set; } 
    public float? DataMore2DataPoint201 { get; internal set; } 
} 

public abstract class AbstractQueryObject<TResult> : IQueryObject<TResult> 
{ 
    protected ISession Session; 

    public void Configure(object parameter) 
    { 
     /*obviously this NH query object implementation has to be provided with an ISession before it executes. 
      for that purpose I have an generic repository which holds an ISession and exposes the method 
      public interface IRepository { .. TResult ExecuteQuery<TResult>(IQueryObject<TResult> query); .. } 
      and has the usual Save<T>/Delete<T> methods. 
      obviously, to get the results you use: var data = repository.ExecuteQuery(new DataQuery()); 
      i like this pattern a lot, so forgive my promotion :) 
      now, one repo instance keeps alive its session, so all entities from it are from the same isession. 
      when you dispose the repo, you kill the session. if you use DI in your classes, the repo instance is injected. 
      very perky is that your classes depend on the iqo and irepo ifcs which means you can swap them for EF which sometimes we do:) 
      easy unit testing.. etc.. */ 

     if (!(parameter is ISession)) 
      throw new ArgumentException("Argument of wrong type."); 

     this.Session = parameter as ISession; 
    } 

    public abstract TResult GetResult(); 
} 

public interface IQueryObject<TResult> 
{ 
    void Configure(object parameter); 

    TResult GetResult(); 
} 
+0

太棒了!現在我有一個線索如何前進。我希望將選擇減少到單個選擇語句。我期待着放棄這一點。 – faldeland

+0

在運行時獲取InvalidOperationException ...從範圍''引用類型爲'MyType'的變量'p',但未定義 – faldeland

+0

我添加了新答案後能夠獲得過去的「未定義」錯誤。現在我得到一個運行時錯誤,dm1和dm2爲空。有什麼想法嗎? – faldeland

0

對接受答案的這種修改讓我想去哪裏。結果是一條select語句,它返回我所需要的...

var list = session.QueryOver<Data>() 
          .JoinAlias(data => data.DataMore1,() => dm1, NHibernate.SqlCommand.JoinType.LeftOuterJoin, Expression.Eq("Segment", (byte)0)) 
          .JoinAlias(data => data.DataMore2,() => dm2, NHibernate.SqlCommand.JoinType.LeftOuterJoin, Expression.Eq("Segment", (byte)0)) 
          .List<Data>() 
          });