Multiple Conditions in Nhibernate Left Join - sql

Please need some help converting this sql query to nhibernate
select a.ID, count(b.ID)
from appusers a
left join weeklytasks b on a.ID = b.TaskOwner and b.taskstatus = 1
group by a.ID

It's difficult to answer without knowing your entities, mappings, used technology(ICreteria API, QueryOver, Linq).
But I can suggest this solution using QueryOver:
AppUser ownerAlias = null;
WeeklyTask taskAlias = null;
var result = Session.QueryOver(() => taskAlias)
.JoinAlias(x => x.TaskOwner,
() => ownerAlias,
NHibernate.SqlCommand.JoinType.RightOuterJoin,
Restrictions.Where(() => taskAlias.Status == 1))
.SelectList(list => list
.SelectGroup(x => ownerAlias.Id)
.SelectCount(x => x.Id))
.List<object[]>();
or this:
var result = Session.QueryOver<WeeklyTask>()
.Where(x => x.Status == 1)
.Right.JoinQueryOver(x => x.TaskOwner)
.SelectList(list => list
.SelectGroup(x => x.TaskOwner.Id)
.SelectCount(x => x.Id))
.List<object[]>();
Please notice that in this approach your WeeklyTask entity must contains mapped reference to AppUser entity.

Related

nHibernate join alias projecting some fields

I have the following query in nHibernate, which works really well.
Table1 table1Alias = null;
Table2 table2Alias = null;
Table3 table3Alias = null;
Table4 table4Alias = null;
Table5 table5Alias = null;
Table6 table6Alias = null;
var resultTable = session.QueryOver<Table1>()
.JoinAlias(x => x.Table2, () => table2Alias, NHibernate.SqlCommand.JoinType.LeftOuterJoin)
.JoinAlias(() => table2Alias.Table3, () => itemDesctable3AliasriptionAlias, NHibernate.SqlCommand.JoinType.LeftOuterJoin)
.JoinAlias(() => table2Alias.Table4, () => table4Alias, NHibernate.SqlCommand.JoinType.LeftOuterJoin)
.JoinAlias(x => x.Table5, () => table5Alias, NHibernate.SqlCommand.JoinType.LeftOuterJoin)
.JoinAlias(() => table5Alias.Table6, () => table6Alias, NHibernate.SqlCommand.JoinType.LeftOuterJoin)
.Where(x => x.Id == inputId)
.TransformUsing(Transformers.DistinctRootEntity)
.SingleOrDefault();
the problem is, i suspect i'll run into performance issues because
some of the tables have a lot of columns.
So here's the question, let's say I only need 1 field from "Table 5", but
fields from the rest of the table, do i have to do manual projections for all tables? or is there a way to only do manual projection for one table but left the rest as is?

NHibernate QueryOver subquery to return only newest record

I have a query in Nhibernate QueryOver which brings back a collection of episode objects (episode being a spell of care) which in turn has a collection of episode statuses as a property of each episode. However I want to change this so that each episode only brings back the latest status update for that episode instead of all of them.
The SQL to do this is as follows:
SELECT *
FROM DIPEpisode e
INNER JOIN DIPEpisodeStatus s on s.EpisodeID = e.SequenceID
WHERE e.ClientID = '1000001'
AND s.SequenceID IN (
SELECT TOP 1 SequenceID
FROM DIPEpisodeStatus s
WHERE s.EpisodeID = e.SequenceID
ORDER BY StatusRecordedDate DESC
)
I have written the following query which gives me almost exactly what I need
var statuses =
QueryOver.Of<DIPEpisodeStatus>()
.OrderBy(x => x.StatusRecordedDate).Desc
.Select(x => x.Id).Take(1);
DIPEpisodeStatus statusAlias = null;
return
session.QueryOver<DIPEpisode>()
.JoinQueryOver(x => x.DIPEpisodeStatuss, () => statusAlias)
.Fetch(x => x.AgencyID).Eager
.Fetch(x => x.DIPEpisodeStatuss).Eager
.Where(e => e.ClientID.Id == this.clientId)
.WithSubquery.WhereProperty(x => x.Id).Eq(statuses)
.List();
This generates the following SQL:
SELECT *
FROM DIPEpisode this_
inner join DIPEpisodeStatus statusalia1_
on this_.SequenceID = statusalia1_.EpisodeID
WHERE statusalia1_.ClientID = '1000001' /* #p0 */
and statusalia1_.SequenceID = (SELECT TOP (1 /* #p1 */) this_0_.SequenceID as y0_
FROM DIPEpisodeStatus this_0_
ORDER BY this_0_.StatusRecordedDate desc)
As you can see, the only thing missing is the where clause from the subquery. What changes do I need to make to the query in order to generate this extra where clause and pull back only the most recent status update?
Thanks
Ben
the collection DIPEpisodeStatuss is always initialized with all entities because it would break changetracking otherwise. you could either define a filter for the collection or return a DTO with what you want. Also the fetch will be ignored because it can not eager load and filter in one sql statement.
NHibernate filters are explained here
defining Filters in FNH
how it would be done with a DTO
// assuming SequneceID and StatusRecordedDate correlates
var subquery = QueryOver.Of<DIPEpisode>()
.Where(e => e.ClientID.Id == this.clientId)
.JoinAlias(e => e.DIPEpisodeStatuss, () => statusAlias)
.Select(Projections.Max(() => statusAlias.SequenceID));
// or as in question
var subquery = QueryOver.Of<DIPEpisode>()
.Where(e => e.ClientID.Id == this.clientId)
.JoinAlias(e => e.DIPEpisodeStatuss, () => statusAlias)
.OrderByDescending(() => statusAlias.StatusRecordedDate)
.Select(() => statusAlias.SequenceID)
.Take(1);
DIPEpisodeDto dto = null;
DIPEpisodeStatus statusAlias = null;
return session.QueryOver<DIPEpisode>()
.Where(e => e.ClientID.Id == this.clientId)
.JoinQueryOver(e => e.DIPEpisodeStatuss, () => statusAlias)
.WithSubquery.WhereProperty(estatus => estatus.Id).Eq(statuses)
.SelectList(list => list
.Select(e => e.Whatever).WithAlias(() => dto.Whatever)
.Select(() => statusAlias.SquenceId).WithAlias(() => dto.StatusId)
...
)
.TransFormUsing(Transformers.AliasToBean<DIPEpisodeDto>())
.List();
or using LINQ
var query = from e in session.Query<DIPEpisode>()
from s in e.DIPEpisodeStatuss
where e.ClientID.Id == this.clientId
where s.Id == (
from e2 in session.Query<DIPEpisode>()
from s2 in e2.DIPEpisodeStatuss
orderby s2.StatusRecordedDate descending
select s2.Id)
.First()
select new DIPEpisodeDto
{
e.Prop1,
Status = s,
};
return query.List<DIPEpisodeDto>();

Multiple fetch in ThenFetch

I've an associated entity with <many-to-one> and that entity has two <many-to-one> that I want to fetch at once. I can achieve this by this query:
var tshead = session.Query<MainEntity>()
.Fetch(r=>r.FirstAssoc).ThenFetch(p=>p.Other)
.Fetch(r=>r.FirstAssoc).ThenFetch(p=>p.Another)
.Take(10)
.ToList();
As you can see I had to wrote twice .Fetch(r=>r.FirstAssoc)
I'm sure I can avoid this but I cant figure out how. Any idea ?
Thanks !
If you select a specific 'MainEntity' then you can do it using QueryOver like so:
FirstAssoc firstAssoc = null;
Other other = null;
Another another = null;
tshead = session.QueryOver<MainEntity>()
.Where(x => x.Id == id)
.JoinAlias(x => x.FirstAssoc, () => firstAssoc)
.JoinAlias(() => firstAssoc.Other, () => other)
.JoinAlias(() => firstAssoc.Another, () => another)
.SingleOrDefault();
I've written about it here:
http://www.philliphaydon.com/2011/04/nhibernate-querying-relationships-are-depth/
You can't do:
One-Many-Many
only One-Many-One.
I'm not sure you can do Many-Many-One, transforming that would be too difficult.

NHibernate QueryOver how to join on non declared relationship

How to do the following join to return Users who have access to a Company given a company id.
The problem is there is no explicit relationship using a User object between UserAccess and User they simply join on the string property Username:
User(Username, Name)
UserAccess(Username, Company)
Company(Id)
Session.QueryOver<Company>()
.Where(c => c.Id == companyId)
.JoinQueryOver<UserCompanyAccess>(u => u.UserAccessList)
.JoinQueryOver<User>(u => **Nope no property, just a string**
could be done with a subquery
var subquery = QueryOver.Of<Company>()
.Where(c => c.Id == companyId)
.JoinQueryOver<UserCompanyAccess>(u => u.UserAccessList)
.Select(uca => uca.UserName);
var users = session.QueryOver<User>()
.WithSubquery.WhereProperty(u => u.Name).In(subquery)
.List();
As of 5.1.0, it is possible to make hibernate generate an actual sql join on an undeclared (unmapped) relationship. E.g. all orders sorted by customer's spending:
var criteria = _session
.CreateCriteria<Order>("order");
criteria
.CreateEntityAlias(
"customer",
Restrictions.EqProperty("order.customerId", "customer._id"),
JoinType.LeftOuterJoin,
typeof(Customer).FullName)
.AddOrder(new Order("customer._lifetimeSpending", ascending:false));
return criteria.List<Order>();
Also possible with QueryOver (sample from NHibernate docs):
Cat cat = null;
Cat joinedCat = null;
var uniquelyNamedCats = sess.QueryOver<Cat>(() => cat)
.JoinEntityAlias(
() => joinedCat,
() => cat.Name == joinedCat.Name && cat.Id != joinedCat.Id,
JoinType.LeftOuterJoin)
.Where(() => joinedCat.Id == null)
.List();

How to do subqueries in nhibernate?

I need to do a subquery on a sub collection but I can't get it to work.
I tried this
Task tAlias = null;
List<Task> result = session.QueryOver<Task>(() => tAlias)
.Where(Restrictions.In(Projections.Property(() => tAlias.Course.Id), courseIds))
.WithSubquery.WhereExists(QueryOver.Of<CompletedTask>().Where(x => x.Student.StudentId == settings.StudentId))
().ToList();
Yet I get
Cannot use subqueries on a criteria
without a projection.
session.QueryOver<Task>(() => tAlias)
.WhereRestrictionsOn(x => x.Course.Id).IsIn(courseIds)
.WithSubquery.WhereExists(QueryOver.Of<CompletedTask>()
.Where(x => x.id == tAlias.id) //not sure how you need to link Task to CompletedTask
.Where(x => x.Student.StudentId == settings.StudentId)
.Select(x => x.id)) //exists requires some kind of projection (i.e. select clause)
.List<Task>();
or if you only want the completedtask then just...
Task taskAlias = null;
session.QueryOver<CompletedTask>()
.JoinAlias(x => x.Task, () => taskAlias)
.WhereRestrictionsOn(() => taskAlias.Course.Id).IsIn(courseIds)
.Where(x => x.Student.StudentId == settings.StudentId)
.List<CompletedTask>();
or look into setting up a student filter on the Task.CompletedTasks collection. I've never used this feature before. I believe you have to enable the filter and set the student parameter before you run the query. Then your Task object would only contain completedTasks by that student...
http://nhibernate.info/doc/nh/en/index.html#filters