NHibernate QueryOver group by 不选择按列分组
NHibernate QueryOver group by without selecting the grouped by column
有如下查询:
var subquery = SessionFactory.GetCurrentSession()
.QueryOver<SomeEntity>()
.Where(_ => _.SomeOtherEntity.Id == someId)
.SelectList(list => list
.SelectGroup(x => x.SomeGroupByProperty)
.SelectMax(x => x.MaxPerGroupProperty))
.List<dynamic>();
生成的 sql 是 select SomeGroupByProperty
和 MaxPerGroupProperty
的最大值。是否有可能让它在 SomeGroupByProperty
上分组,但最多只有 select 个 MaxPerGroupProperty
?这是为了使用包含在父查询中的子查询结果。
我知道,我只是在展示备选方案,而不是给出答案。但是如果我们需要在另一个查询中使用这样的子查询,我们可以:
- 使用外部查询的一些设置过滤内部查询
- 使用
Exists
(可以 return 我们想要多少列)
所以,我们需要先定义Alias
(外部查询)然后做一些过滤:
有如下查询:
SomeOtherEntity otherEntity = null; // alias of the outer query
var subquery = QueryOver
.QueryOver.Of()<SomeEntity>()
.Where(_ => _.SomeOtherEntity.Id == someId)
.Where(_ => _.SomeOtherEntity.Id == otherEntity.Id) // here we consume outer alias
.SelectList(list => list
.SelectGroup(x => x.SomeGroupByProperty)
.SelectMax(x => x.MaxPerGroupProperty)
);
之后,在外部 (root) 查询中,我们可以这样使用它:
var query = session
.QueryOver(() => otherEntity)
.WithSubquery
.WhereExists(subquery);
这是超级简化的解决方案,带有 WHERE 子句。我们很可能需要在 HAVING 子句中应用该过滤器。这是一个详细的示例:
Query on HasMany reference
因此,子查询将如下所示:
// this WHERE
// .Where(_ => _.SomeOtherEntity.Id == otherEntity.Id) // here we consume
// into this HAVING
.Where(Restrictions.EqProperty(
Projections.Max<SomeOtherEntity >(x => x.MaxPerGroupProperty),
Projections.Property(() => otherEntity.GroupProperty)
))
完整example here
这是 NHibernate jira 中的未解决问题(条件查询):https://nhibernate.jira.com/browse/NH-1426
虽然你可以这样做
var subquery =
QueryOver.Of<SomeEntity>()
.Where(_ => _.SomeOtherEntity.Id == someId)
.Select(
Projections.ProjectionList()
.Add(Projections.SqlGroupProjection("max(MaxPerGroupProperty) as maxAlias", "SomeGroupByProperty",
new string[] { "maxAlias" }, new IType[] { NHibernate.NHibernateUtil.Int32 })));
var parentQuery = session.QueryOver<SomeEntity2>()
.WithSubquery.WhereProperty(x => x.MaxPerGroupPropertyReference).In(subquery).List();
不如使用实体属性那么漂亮,但它确实有效。
您可以使用自己的投影来实现,这类似于 NHibernate ProjectionList
,只是 ToSqlString
、GetTypes
和 GetTypedValues
方法的实现略有不同。
public static class CustomProjections
{
public static ProjectionWithGroupByWithoutSelectProjection GroupByWithoutSelect(IProjection projection, params Expression<Func<object>>[] groupByExpressions)
{
var projectionRet = new ProjectionWithGroupByWithoutSelectProjection();
projectionRet.Add(projection);
foreach (var groupByExpression in groupByExpressions)
{
projectionRet.Add(Projections.Group(groupByExpression));
}
return projectionRet;
}
}
public class ProjectionWithGroupByWithoutSelectProjection : IProjection
{
// because ProjectionList constructor is protected internal
private class ProjectionListCustom : ProjectionList
{
}
private readonly ProjectionList _projectionList = new ProjectionListCustom();
protected internal ProjectionWithGroupByWithoutSelectProjection()
{
}
public ProjectionWithGroupByWithoutSelectProjection Add(IProjection proj)
{
_projectionList.Add(proj);
return this;
}
public ProjectionWithGroupByWithoutSelectProjection Add(IProjection projection, string alias)
{
_projectionList.Add(projection, alias);
return this;
}
public ProjectionWithGroupByWithoutSelectProjection Add<T>(IProjection projection, Expression<Func<T>> alias)
{
_projectionList.Add(projection, alias);
return this;
}
public IType[] GetTypes(ICriteria criteria, ICriteriaQuery criteriaQuery)
{
return _projectionList[0].GetTypes(criteria, criteriaQuery);
}
public SqlString ToSqlString(ICriteria criteria, int loc, ICriteriaQuery criteriaQuery, IDictionary<string, IFilter> enabledFilters)
{
return _projectionList[0].ToSqlString(criteria, loc, criteriaQuery, enabledFilters);
}
public SqlString ToGroupSqlString(ICriteria criteria, ICriteriaQuery criteriaQuery, IDictionary<string, IFilter> enabledFilters)
{
return _projectionList.ToGroupSqlString(criteria, criteriaQuery, enabledFilters);
}
public string[] GetColumnAliases(int position, ICriteria criteria, ICriteriaQuery criteriaQuery)
{
return _projectionList.GetColumnAliases(position, criteria, criteriaQuery);
}
public string[] GetColumnAliases(string alias, int position, ICriteria criteria, ICriteriaQuery criteriaQuery)
{
return _projectionList.GetColumnAliases(alias, position, criteria, criteriaQuery);
}
public IType[] GetTypes(string alias, ICriteria criteria, ICriteriaQuery criteriaQuery)
{
return _projectionList[0].GetTypes(alias, criteria, criteriaQuery);
}
public string[] Aliases => _projectionList.Aliases;
public override string ToString()
{
return _projectionList.ToString();
}
public bool IsGrouped => _projectionList.IsGrouped;
public bool IsAggregate => _projectionList.IsAggregate;
public TypedValue[] GetTypedValues(ICriteria criteria, ICriteriaQuery criteriaQuery)
{
return _projectionList[0].GetTypedValues(criteria, criteriaQuery);
}
}
有如下查询:
var subquery = SessionFactory.GetCurrentSession()
.QueryOver<SomeEntity>()
.Where(_ => _.SomeOtherEntity.Id == someId)
.SelectList(list => list
.SelectGroup(x => x.SomeGroupByProperty)
.SelectMax(x => x.MaxPerGroupProperty))
.List<dynamic>();
生成的 sql 是 select SomeGroupByProperty
和 MaxPerGroupProperty
的最大值。是否有可能让它在 SomeGroupByProperty
上分组,但最多只有 select 个 MaxPerGroupProperty
?这是为了使用包含在父查询中的子查询结果。
我知道,我只是在展示备选方案,而不是给出答案。但是如果我们需要在另一个查询中使用这样的子查询,我们可以:
- 使用外部查询的一些设置过滤内部查询
- 使用
Exists
(可以 return 我们想要多少列)
所以,我们需要先定义Alias
(外部查询)然后做一些过滤:
有如下查询:
SomeOtherEntity otherEntity = null; // alias of the outer query
var subquery = QueryOver
.QueryOver.Of()<SomeEntity>()
.Where(_ => _.SomeOtherEntity.Id == someId)
.Where(_ => _.SomeOtherEntity.Id == otherEntity.Id) // here we consume outer alias
.SelectList(list => list
.SelectGroup(x => x.SomeGroupByProperty)
.SelectMax(x => x.MaxPerGroupProperty)
);
之后,在外部 (root) 查询中,我们可以这样使用它:
var query = session
.QueryOver(() => otherEntity)
.WithSubquery
.WhereExists(subquery);
这是超级简化的解决方案,带有 WHERE 子句。我们很可能需要在 HAVING 子句中应用该过滤器。这是一个详细的示例:
Query on HasMany reference
因此,子查询将如下所示:
// this WHERE
// .Where(_ => _.SomeOtherEntity.Id == otherEntity.Id) // here we consume
// into this HAVING
.Where(Restrictions.EqProperty(
Projections.Max<SomeOtherEntity >(x => x.MaxPerGroupProperty),
Projections.Property(() => otherEntity.GroupProperty)
))
完整example here
这是 NHibernate jira 中的未解决问题(条件查询):https://nhibernate.jira.com/browse/NH-1426
虽然你可以这样做
var subquery =
QueryOver.Of<SomeEntity>()
.Where(_ => _.SomeOtherEntity.Id == someId)
.Select(
Projections.ProjectionList()
.Add(Projections.SqlGroupProjection("max(MaxPerGroupProperty) as maxAlias", "SomeGroupByProperty",
new string[] { "maxAlias" }, new IType[] { NHibernate.NHibernateUtil.Int32 })));
var parentQuery = session.QueryOver<SomeEntity2>()
.WithSubquery.WhereProperty(x => x.MaxPerGroupPropertyReference).In(subquery).List();
不如使用实体属性那么漂亮,但它确实有效。
您可以使用自己的投影来实现,这类似于 NHibernate ProjectionList
,只是 ToSqlString
、GetTypes
和 GetTypedValues
方法的实现略有不同。
public static class CustomProjections
{
public static ProjectionWithGroupByWithoutSelectProjection GroupByWithoutSelect(IProjection projection, params Expression<Func<object>>[] groupByExpressions)
{
var projectionRet = new ProjectionWithGroupByWithoutSelectProjection();
projectionRet.Add(projection);
foreach (var groupByExpression in groupByExpressions)
{
projectionRet.Add(Projections.Group(groupByExpression));
}
return projectionRet;
}
}
public class ProjectionWithGroupByWithoutSelectProjection : IProjection
{
// because ProjectionList constructor is protected internal
private class ProjectionListCustom : ProjectionList
{
}
private readonly ProjectionList _projectionList = new ProjectionListCustom();
protected internal ProjectionWithGroupByWithoutSelectProjection()
{
}
public ProjectionWithGroupByWithoutSelectProjection Add(IProjection proj)
{
_projectionList.Add(proj);
return this;
}
public ProjectionWithGroupByWithoutSelectProjection Add(IProjection projection, string alias)
{
_projectionList.Add(projection, alias);
return this;
}
public ProjectionWithGroupByWithoutSelectProjection Add<T>(IProjection projection, Expression<Func<T>> alias)
{
_projectionList.Add(projection, alias);
return this;
}
public IType[] GetTypes(ICriteria criteria, ICriteriaQuery criteriaQuery)
{
return _projectionList[0].GetTypes(criteria, criteriaQuery);
}
public SqlString ToSqlString(ICriteria criteria, int loc, ICriteriaQuery criteriaQuery, IDictionary<string, IFilter> enabledFilters)
{
return _projectionList[0].ToSqlString(criteria, loc, criteriaQuery, enabledFilters);
}
public SqlString ToGroupSqlString(ICriteria criteria, ICriteriaQuery criteriaQuery, IDictionary<string, IFilter> enabledFilters)
{
return _projectionList.ToGroupSqlString(criteria, criteriaQuery, enabledFilters);
}
public string[] GetColumnAliases(int position, ICriteria criteria, ICriteriaQuery criteriaQuery)
{
return _projectionList.GetColumnAliases(position, criteria, criteriaQuery);
}
public string[] GetColumnAliases(string alias, int position, ICriteria criteria, ICriteriaQuery criteriaQuery)
{
return _projectionList.GetColumnAliases(alias, position, criteria, criteriaQuery);
}
public IType[] GetTypes(string alias, ICriteria criteria, ICriteriaQuery criteriaQuery)
{
return _projectionList[0].GetTypes(alias, criteria, criteriaQuery);
}
public string[] Aliases => _projectionList.Aliases;
public override string ToString()
{
return _projectionList.ToString();
}
public bool IsGrouped => _projectionList.IsGrouped;
public bool IsAggregate => _projectionList.IsAggregate;
public TypedValue[] GetTypedValues(ICriteria criteria, ICriteriaQuery criteriaQuery)
{
return _projectionList[0].GetTypedValues(criteria, criteriaQuery);
}
}