nhibernate group by join查询

时间:2013-01-31 15:06:38

标签: nhibernate join group-by aggregate-functions queryover

我有以下实体:

public class Shift
{
    public virtual int ShiftId { get; set; }
    public virtual string ShiftDesc { get; set; }

    public virtual IList<ShiftHistory> ShiftHistory { get; set; }
}

public class ShiftHistory
{
    public virtual System.DateTime ShiftStartLocal { get; set; }
    public virtual System.DateTime ShiftEndLocal { get; set; }

    public virtual Zone Zone { get; set; }
    public virtual Shift Shift { get; set; }

    public virtual int RowId { get; set; }
}

public class Zone
{
    public virtual int EntId { get; set; }
    public virtual string EntName { get; set; }
}

班次是班次定义,区域是人们工作的地方,班次历史是班次在班次运行时的历史数据。

我需要的是获取所有区域的最后一次轮班

/ *编辑:对不起,我很模糊。每个班次都可以在多个区域有历史。我需要的是最新的历史记录,无论区域,每个班次,任何NH查询类型。 * /

但这不是那么简单,至少现在不适合我,而且我花了太多时间在这上面,并最终采取了一个SQL查询:

public class GetAllShiftSchedules : AbstractQueryObject<IList<ShiftScheduleResult>>
{
    public override IList<ShiftScheduleResult> GetResult()
    {   
        //use distinct because of the zones - if a shift is run at multiple zones, it ends at the same time, no? :)
        var sql =
            @"
            select distinct
                sh.ShiftId, s.ShiftDesc, sh.ShiftStartLocal, sh_inner.LatestEndTimeLocal
                from ShiftHistory sh
                  inner join Shift s on s.ShiftId = sh.ShiftId
                  inner join 
                  (
                    select ShiftId, max(ShiftEndLocal) as LatestEndTimeLocal from ShiftHistory group by ShiftId
                  )  
                   sh_inner on sh_inner.ShiftId = sh.ShiftId and sh_inner.LatestEndTimeLocal = sh.ShiftEndLocal
            ";

        var res = this.Session.CreateSQLQuery(sql).List().OfType<object[]>().Select(p => new ShiftScheduleResult(p)).ToList();

        return res;
    }
}

public class ShiftScheduleResult
{
    public int ShiftId { get; private set; }
    public string ShiftDesc { get; private set; }
    public DateTime LastShiftStartLocal { get; private set; }
    public DateTime LastShiftEndLocal { get; private set; }

    internal ShiftScheduleResult(object[] data)
    {
        this.ShiftId = (int)data[0];
        this.ShiftDesc = (string)data[1];
        this.LastShiftStartLocal = (DateTime)data[2];
        this.LastShiftEndLocal = (DateTime)data[3];
    }
}

我最接近的是:

        var innerSubquery =
            QueryOver.Of<ShiftHistory>()
                .Select(
                    Projections.Group<ShiftHistory>(e => e.Shift),
                    Projections.Max<ShiftHistory>(e => e.ShiftEndLocal)); 

        IList<Shift> shifts =
            this.Session.QueryOver<Shift>()
                .JoinQueryOver<ShiftHistory>(p => p.ShiftHistory)
                .Where(Subqueries.WhereProperty<ShiftHistory>(p => p.ShiftEndLocal).Eq(innerSubquery)).List();  

生成了以下sql:

SELECT
...
 FROM
  Shift this_
   inner join ShiftHistory shifthisto1_ on this_.ShiftId=shifthisto1_.ShiftId
 WHERE
  shifthisto1_.ShiftEndLocal = 
  (
  SELECT this_0_.ShiftId as y0_, max(this_0_.ShiftEndLocal) as y1_ FROM ShiftHistory this_0_ GROUP BY this_0_.ShiftId
  )

当然会失败,因为group by select返回多个值,我们无法将其与ShEndLoc进行比较。 如果我可以让分组投影仅返回最大日期,这实际上对我有用。但是,为了使其完整,我们还应该能够通过班次ID进行比较。

我很高兴返回DTO但是包含实际班次或换班历史会很酷。

感谢阅读。

4 个答案:

答案 0 :(得分:3)

        var list = 
            this.Session.Query<Shift>().Select(
                p => new
                {
                    Shift = p,
                    LastShiftHistory =
                        this.Session.Query<ShiftHistory>()
                            .Where(sh => sh.Shift == p)
                            .OrderByDescending(sh => sh.ShiftEndLocal)
                            .Select(sh => sh)
                            .FirstOrDefault()
                })
                .ToList();

应该有效。虽然投影到DTO会更好,因为子查询会延迟加载。

答案 1 :(得分:1)

这样的事情应该有效...你的结果是匿名类型,因为来自不同类的老茧的组合......

   public class QueryPlace()
    {
        List<ShiftHistory> ShiftHistrys = new List<ShiftHistory>();
        List<Shift> Shifts = new List<Shift>();
        List<Zone> Zones = new List<Zone>();

        public object MakeQuery()
        {
            var mshift = (from r in (from sh in session.Query<ShiftHistory>() from sf in session.Query<Shift>() where sh.Shift == sf
                         select new{sh.ShiftStartLocal, sh.ShiftEndLocal, sf.ShiftId, sf.ShiftHistory}) group r by r.ShiftId into g
                        select new {ShiftStartLocal = g.Max(s => s.ShiftStartLocal), ShiftEndLocal = g.Max(s => s.ShiftEndLocal), ShiftId = g.Key}).FirstOrDefault();

            return mshift;
        }
    }

答案 2 :(得分:0)

当DB方案与业务需求相结合导致复杂查询时,更改DB方案可能会更好。 举例来说,在ShiftHistory上添加“活动”标志,或者在最后一个ShiftHistory上的Shift中添加外键。 当然,这可以被认为是一些“去标准化”,可能在DB中具有不连贯的数据(对于相同的Shift有一个以上的活动历史,或者没有指向真实的最后一个的“最后一次”外键)。 但处理这些风险会比维护复杂的查询花费更多吗?我认为这是一个权衡取舍。

答案 3 :(得分:0)

为了完整起见,因为我的这个数据库使用复合id,我无法获得提供的完整查询枪手。

我最终得到的是我的原始查询,用NH编写:

        var list =

        this.Session.Query<Shift>()
            .Select(
                p => 
                new ShiftScheduleResult()
                {
                    ShiftId =  p.ShiftId,
                    ShiftDesc = p.ShiftDesc,
                    LastShiftStartLocal
                        = p.ShiftHistory
                            .OrderByDescending(sh => sh.ShiftEndLocal)
                            .Select(x => x.ShiftStartLocal)
                            .FirstOrDefault(),
                    LastShiftEndLocal
                        = p.ShiftHistory
                            .OrderByDescending(sh => sh.ShiftEndLocal)
                            .Select(x => x.ShiftEndLocal)
                            .FirstOrDefault()
                })
            .ToList();

我相信Zoran的查询也会奏效。 谢谢大家的帮助。