Reputation: 145
I have two tables, in one to many relationship (Quizzes, Comments): a Quiz can have multiple Comments
I need to display inside a page the last 5 comments for each quiz.
Is it possible to extract theses comments using one single query (if no, which is the best way to do it)? Right now I am executing a separate query for each quiz to extract the last 5 comments.
(my hope is to find a single HQL to allow me extracting theses comments)
ps. I am using hibernate/jpa/mysql
Upvotes: 3
Views: 2427
Reputation: 111
I don't think this is possible in hql, but this is my attempt using standard sql:
SELECT
q.id AS quiz_id, c.id AS comment_id, c.text AS comment_text, c.date AS comment_date
FROM
quiz q
JOIN
comments c
ON
q.id = c.quiz_id
WHERE
c.id IN
(SELECT
id
FROM
comments c2
WHERE
c2.quiz_id = q.id
ORDER BY
date desc
LIMIT 5
)
ORDER BY
q.id ASC, c.date ASC
EDIT: The reason why I think this won't be possible in hql is because it does not support LIMIT as far as I know, because it is not standard SQL. This also means that my example is not 100% pure standard SQL, but mysql supports it - and you use mysql, so I think that's ok.
EDIT2: Fixed the SQL, because it was wrong. Now it uses a subselect (I'm not sure if this is fast anymore, but I rely on the query optimizer there ^^). Also tested it on a local database (postgres instead of mysql, but it should work in both).
Upvotes: 0
Reputation: 4564
I wrote a complicated SQL that runs with MySQL ;-)
the basic idee is:
Precondition is that a comment with a newer date
has a higher id
Update: changed SQL. (realized some missing test cases in my small set of test data)
This part of the SQL is later used twice... you should create a View with that.
SELECT
@rownum:=@rownum+1 AS Rank,
c.*
FROM _comments c, (SELECT @rownum:=0) r
ORDER BY c.q_id, c.id
-
SELECT
ranked_c.*
FROM (
SELECT
@rownum:=@rownum+1 AS Rank,
c.*
FROM _comments c, (SELECT @rownum:=0) r
ORDER BY c.q_id, c.id
) ranked_c
INNER JOIN (
SELECT
i.q_id,
MAX(i.Rank) AS LastEntry_id
FROM (
SELECT
@rownum:=@rownum+1 AS Rank,
c.*
FROM _comments c, (SELECT @rownum:=0) r
ORDER BY c.q_id, c.id
) i
GROUP BY i.q_id
) max_c ON ranked_c.q_id = max_c.q_id
WHERE max_c.LastEntry_id - ranked_c.Rank BETWEEN 0 AND 4
alternative where clause: abs(max_c.LastEntry_id - ranked_c.Rank) < 5
--> Solution using a View:
CREATE OR REPLACE VIEW V_RankedComments AS (
SELECT
@rownum:=@rownum+1 AS Rank,
c.*
FROM _comments c, (SELECT @rownum:=0) r
ORDER BY c.q_id, c.id
)
SELECT
ranked_c.*
FROM V_RankedComments ranked_c
INNER JOIN (
SELECT
i.q_id,
MAX(i.Rank) AS LastEntry_id
FROM V_RankedComments i
GROUP BY i.q_id
) max_c ON ranked_c.q_id = max_c.q_id
WHERE max_c.LastEntry_id - ranked_c.Rank BETWEEN 0 AND 4
Upvotes: 2
Reputation: 691635
I think you need to use analytic functions to do that. Here is an example for Oracle.
In your cas, the sql query would look like this :
SELECT quizz_id, comment_id, comment_text FROM (
SELECT c.quizz_id, c.comment_id, c.comment_text, ROW_NUMBER()
OVER (PARTITION BY c.quizz_id ORDER BY c.date DESC) AS rn
FROM comments c)
WHERE rn <= 5 order by quizz_id, rn;
But You won't be able to use HQL for this kind of query.
Upvotes: 0