开发者

Simple but Complicated HQL/SQL Query

开发者 https://www.devze.com 2023-02-13 08:59 出处:网络
I have two tables, in one to many relationship (Quizzes, Comments): a Quiz can have multiple Comments

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 usin开发者_运维问答g 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


I wrote a complicated SQL that runs with MySQL ;-)
the basic idee is:

  • Order the comments and adding a rownum as rank. comments to same quizz have generated ranks with contiguous nummers
  • join a max aggregation with the newest comment per quizz
  • with this added information it's possible to build a where clause to limit comments per quizz

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 


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.


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).

0

精彩评论

暂无评论...
验证码 换一张
取 消