Using HQL with MySQL how can I order the result se

2020-04-21 01:37发布

问题:

Is there any way to write a greatest-n-per-group query in HQL (or potentially with Hibernate Criteria) in a single query?

I'm struggling with a problem that's similar to this:

Schema:

  • Book has a publication_date
  • Book has an Author
  • Author has a Publisher

I have a Publisher in hand, as well as a search date. For all of that Publisher's Authors, I want to find the book that they published most recently before the search date.

I've been trying to get this working (and have looked at many of the other questions under hibernate as well as greatest-n-per-group) but haven't found any examples that work.

In straight MySQL, I'm able to use a subselect to get this:

select * from
(select
        a.id author_id,
        b.id book_id,
        b.publication_date publication_date
   from book b
   join author a on a.id = b.author_id
   join publisher p on p.id = a.publisher_id
  where
    b.publication_date <= '2011-07-01'
    and p.id = 2
order by b.publication_date desc) as t
 group by
    t.author_id

This forces the order by to happen first in the subquery, then the group by happens afterwards and picks the most recently published book as the first item to group by. I get the author ID paired with the book ID and publication date. (I know this isn't a particularly efficient way to do it in SQL, this is just an example of one way to do it in native SQL that's easy to understand).

In hibernate, I've been unable to construct a subquery that emulates this. I also haven't had any luck with trying to use a having clause like this, it returns zero results. If I remove the having clause, it returns the first book in the database (based on it's ID) as the group by happens before the order by:

Book.executeQuery("""
        select b.author, b
        from Book b 
        where b.publicationDate <= :date
        and b.author.publisher = :publisher
        group by b.author
        having max(b.publicationDate) = b.publicationDate
        order by py.division.id
""", [date: date, publisher: publisher])

Is there any way to get hibernate to do what I want without having to spin through objects in memory or dive back down to raw SQL?

This is against a MySQL database and using Hibernate through Grails if it matters.

回答1:

For that, you need a SQL window function. There is no way to do it in Hibernate/HQL, HQL doesn't support window functions.

greatest-n-per-group tag has the correct answers. For instance, this approach is pretty readable, though not always optimal.