我有以下实体:
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但是包含实际班次或换班历史会很酷。
感谢阅读。
答案 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的查询也会奏效。 谢谢大家的帮助。