I've an entity model like this:
public class Request
{
public virtual IList<Response> Responses { get; set; }
}
public class Response
{
public virtual DateTime Timestamp { get; set; }
public virtual bool Success { get; set; }
}
I'm trying to create a Query that will give me all Request where its latest Response (regarding to its Timestamp) is sucess. How can this be done?
As almost always, NHibernate does have answer for this. What we are here trying to achieve would be a SQL Statement lookin like this:
// final Request selection
SELECT request.[RequestId]
FROM [Request] request
// Only requests, which are successful, and have Max(date)
WHERE request.[RequestId] IN
(
SELECT successResponse.RequestId as y0_
FROM [Response] successResponse
// response which max date is equal to the upper response
// and which RequestId corresponds with supper upper Request
WHERE EXISTS
(
SELECT maxResponse.RequestId as y0_
, max(maxResponse.[DateTime]) as y1_
FROM [Response] maxResponse
// do the MAX only for current Request
WHERE maxResponse.RequestId = successResponse.RequestId
GROUP BY maxResponse.RequestId
// assure that the Response match is on the max DateTime
HAVING max(maxResponse.[DateTime]) = successResponse.[DateTime]
)
AND successResponse.[Success] = 1
)
Notes:
RequestId
//
comment instead of SQL --
And now the magic of NHibernate and QueryOver:
// This declaration will allow us, to use a reference from middle SELECT
// in the most deeper SELECT
Response response = null;
// the most INNER SELECT
var maxSubquery = QueryOver.Of<Response>()
.SelectList(l => l
.SelectGroup(item => item.RequestId)
.SelectMax(item => item.DateTime)
)
// WHERE Clause
.Where(item => item.RequestId == response.RequestId)
// HAVING Clause
.Where(Restrictions.EqProperty(
Projections.Max<Response>(item => item.DateTime),
Projections.Property(() => response.DateTime)
));
// the middle SELECT
var successSubquery = QueryOver.Of<Response>(() => response)
// to filter the Request
.Select(res => res.RequestId)
.WithSubquery
.WhereExists(maxSubquery)
// now only these wich are successful
.Where(success => success.Success == true)
;
At this moment we have to inner SUB SELECTs, nested. let's use them:
// the most outer SELECT
var query = session.QueryOver<Request>();
query.WithSubquery
// our Request ID is IN(...
.WhereProperty(r => r.ID)
.In(successSubquery);
var list = query
.List<Request>();
Final notes, I am not discussing the concept. Not the performance. I would use rather a setting on response "IsActive" and make it easier ... this is just the answer how to do that...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With