问题
In short: Given an aggregate query (one with Max, Min, Count, etc) in NHibernate, how can you modify the query to also return the full record associated with the aggregated value?
My example: I have 2 tables: People (primary key: PersonId) with a 1-to-many relationship to Events (primary key: EventId; other columns: PersonId, EventDate).
I want to select the last event per person and generate a list of these events. The SQL for last event per person would be something like SELECT PersonId, Max(EventDate) FROM ... GROUP BY PersonId. So far the NHibernate query looks like:
ICriteria criteria = session.CreateCriteria<Event>()
.SetProjection(Projections.ProjectionList()
.Add(Projections.GroupProperty("PersonId"))
.Add(Projections.Max("EventDate"))
);
Now what I really need is the full event info. One solution, in theory, is to essentially join the above criteria to the Events table by PersonId and the max EventDate (easy enough in plain SQL). However I'm at a loss of how to perform this in NHibernate.
I'm open to any suggestion (HQL, LINQ, etc.) so long as it avoids stored procedures and views and is limited to 1 or just a few queries. Issuing a query per Person will not be scalable or performant in my case.
回答1:
I hope you are also open to QueryOver (this can be converted to ICriteria)...
Event eventAlias = null;
var topEventsByPerson = Session.QueryOver<Event>(() => eventAlias)
.WithSubquery.WhereProperty(x => x.EventId).Eq(QueryOver.Of<Event>()
.Where(x => x.Person == eventAlias.Person)
.OrderBy(x => x.EventDate).Desc
.Select(x => x.EventId)
.Take(1))
.List();
回答2:
Although I prefer dotjoe's answer I wasn't able to implement it because of an ArgumentNullException thrown when using a QueryOver alias. I suspect this is a bug in NHibernate 3.1.
Instead I found a solution using ISession.CreateSQLQuery() where I write the query in plain SQL:
var results = session.CreateSQLQuery(@"
SELECT Events.*
FROM Events
join (
SELECT MAX(EventDate) as MaxEventDate, PersonId
FROM Events
GROUP BY PersonId
)
as q_LastEventsPerPerson
on (Events.EventDate = q_LastEventsPerPerson.MaxEventDate)
and (Events.PersonId = q_LastEventsPerPerson.PersonId)
")
.AddEntity(typeof(Event))
.List<Event>();
The AddEntity() method is key here. It causes the automatic mapping of the results to objects of type Event.
I do not recommend this approach unless you have no alternative.
来源:https://stackoverflow.com/questions/8154632/nhibernate-select-full-records-from-aggregates