【问题标题】:Simple but Complicated HQL/SQL Query简单但复杂的 HQL/SQL 查询
【发布时间】:2011-07-02 14:36:15
【问题描述】:

我有两张表,一对多关系(测验、评论):一个测验可以有多个评论

我需要在页面内显示每个测验的最后 5 个 cmets。

是否可以使用一个查询来提取这些 cmets(如果没有,这是最好的方法)?现在,我正在为每个测验执行一个单独的查询,以提取最后 5 个 cmets。

(我希望能找到一个单独的 HQL 来让我提取这些 cmets)

ps。我正在使用hibernate/jpa/mysql

【问题讨论】:

标签: java mysql sql jpa hql


【解决方案1】:

我认为您需要使用分析函数来做到这一点。这是example for Oracle

在您的 cas 中,sql 查询如下所示:

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;

但您将无法使用 HQL 进行此类查询。

【讨论】:

    【解决方案2】:

    我写了一个复杂的 SQL 运行 MySQL ;-)
    基本思路是:

    • 对 cme​​ts 进行排序并添加 rownum 作为排名。同一测验的 cmets 生成的排名具有连续的数字
    • 使用每个测验的最新评论加入最大聚合
    • 使用此附加信息,可以构建 where 子句来限制每次测验的 cmets

    前提条件date较新的评论具有较高的id

    更新:更改了 SQL。 (在我的一小部分测试数据中发现了一些缺失的测试用例)
    这部分 SQL 稍后会使用两次...您应该使用它创建一个视图。

    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 
    

    替代 where 子句:abs(max_c.LastEntry_id - ranked_c.Rank) &lt; 5

    --> 使用视图的解决方案:

    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 
    

    【讨论】:

    • 对频繁的编辑感到抱歉。我最初的测试数据没有涵盖现实生活中的用例,我发现我的代码在每个新用例中都会出现问题。
    【解决方案3】:

    我认为这在 hql 中是不可能的,但这是我使用标准 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
    

    编辑:我认为这在 hql 中不可能的原因是因为据我所知它不支持 LIMIT,因为它不是标准 SQL。这也意味着我的示例不是 100% 纯标准 SQL,但 mysql 支持它 - 而你使用 mysql,所以我认为没关系。

    EDIT2:修正了 SQL,因为它是错误的。现在它使用了一个子选择(我不确定这是否很快,但我依赖那里的查询优化器^^)。还在本地数据库上对其进行了测试(postgres 而不是 mysql,但它应该适用于两者)。

    【讨论】:

    • 这将返回 5 个最新的 cmets,但不返回 5 个最新的 cmets每个测验
    • 是的,我在本地测试中也注意到了这一点。现在我修好了。
    • 好主意。但 MySQL 有一个讨厌的限制:(测试 MySQL 5.1 和 5.5)[Error Code: 1235, SQL State: 42000] This version of MySQL doesn't yet support 'LIMIT &amp; IN/ALL/ANY/SOME subquery'
    • mmh,好的,所以这使我的示例无法正常工作。好的,所以您的帖子 bw_üezi 是唯一适用于 mysql 的帖子。如果我已经可以投票,我会投票赞成你的答案。
    猜你喜欢
    • 2016-08-18
    • 2021-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-08
    相关资源
    最近更新 更多