Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NHibernate HQL SELECT TOP in sub query

Tags:

hql

nhibernate

Is there a way of using SetMaxResult() on a sub query? Im writing a query to return all the order items belonging to the most recent order. So I need to limit the number of records on the sub query.

The equivalent sql looks something like:

SELECT i.*
FROM tbl_Orders o
JOIN tbl_OrderItems i on i.OrderId = o.Id
WHERE
o.Id in (SELECT TOP 1 o.Id FROM tbl_Orders o orderby o.Date desc)

Im using hql specifically because criteria api doesnt let you project another domain object (Im querying on orders but want to return order items)

I know that hql doesnt accept "SELECT TOP", but if I use SetMaxResult() it will apply to the outer query, not the subquery.

Any ideas?

like image 503
Andy Avatar asked Jan 12 '10 11:01

Andy


People also ask

Can we use ORDER BY in subquery?

An ORDER BY command cannot be used in a subquery, although the main query can use an ORDER BY. The GROUP BY command can be used to perform the same function as the ORDER BY in a subquery. Subqueries that return more than one row can only be used with multiple value operators such as the IN operator.

Is subquery supported in HQL?

Even correlated subqueries (subqueries that refer to an alias in the outer query) are allowed. Note that HQL subqueries can occur only in the select or where clauses. Note that subqueries can also utilize row value constructor syntax.

Why can t you use an ORDER BY in a subquery?

A "table" (and subquery in the FROM clause too) is - according to the SQL standard - an unordered set of rows. Rows in a table (or in a subquery in the FROM clause) do not come in any specific order. That's why the optimizer can ignore the ORDER BY clause that you have specified.


3 Answers

From NHibernate 3.2 you could use SKIP n / TAKE n in hql at the end of the query. You query will be:

SELECT i.*
FROM tbl_Orders o
JOIN tbl_OrderItems i on i.OrderId = o.Id
WHERE
o.Id in (SELECT o.Id FROM tbl_Orders o orderby o.Date desc take 1)
like image 132
Pavel Bakshy Avatar answered Sep 20 '22 01:09

Pavel Bakshy


Just query the orders (and use SetMaxResult) and do a 'fetch join' to ensure all orderitems for the selected orders are loaded straight away. On the returned orders you can then access the order items without this resulting in a new SQL statement being sent to the database.

like image 27
Fried Hoeben Avatar answered Sep 22 '22 01:09

Fried Hoeben


I encountered this problem too, but didn't found a solution using HQL...

Subqueries with top would be very nice, since this is faster then doing a full join first. When doing a full join first, the SQL Servers join the table first, sort all rows and select the top 30 then. With the subselect, the top 30 column of one table are taken and then joined with the other table. This is much faster!

My query with Subselect takes about 1 second, the one with the join and sort takes 15 seconds! So join wasn't an option.

I ended up with two queries, first the subselect:

IQuery q1 = session.CreateQuery("select id from table1 order by id desc");
q1.SetMaxResults(100);

And then the second query

IQuery q2 = session.CreateQuery("select colone, coltwo from table2 where table1id in (:subselect)");
q2.SetParameterList("subselect", q1.List());
like image 31
falstaff Avatar answered Sep 21 '22 01:09

falstaff