【问题标题】:SQL generated by jOOQ for nested select doesn't work in MySQL/MariaDBjOOQ 为嵌套选择生成的 SQL 在 MySQL/MariaDB 中不起作用
【发布时间】:2018-10-13 00:24:51
【问题描述】:

对于一个经典的排名问题(每个玩家最好的 3 个结果的总和),我使用 jOOQ DSLScala(嵌入作为 select 值并加入更复杂的查询):

val tr1 = TOUR_RESULT.as("tr1")
val tr2 = TOUR_RESULT.as("tr2")
val inner:Table[Record1[java.lang.Integer]] = DSL.select(count().as("count")).from(tr2).where(tr1.PLAYER_ID.eq(tr2.PLAYER_ID).and(tr1.NSP_SCORE.le(tr2.NSP_SCORE))).asTable("tr3") 
val result = sql.select(tr1.PLAYER_ID,tr1.NSP_SCORE.sum().as("score"))
    .from(tr1)
    .where(inline(3).gt(sql.selectQuery(inner)))
    .groupBy(tr1.PLAYER_ID)
    .orderBy(2)
    .execute()

我目前的问题(使用 MariaDB 10 和 jOOQ 3.9)是让这个生成的查询完全适用于 MySQL,并按“分数”(第 2 列)降序排序。 我相信我可以使用原始 SQL 语句重写整个语句并转换结果(因此绕过 jOOQ API)。但是,我希望尽可能长时间地使用编译器和 jOOQ API 作为我的牧羊人。因此,如果我必须再次触摸此查询,也许有一个解决方案在将来仍然可以阅读。

可以在下面找到说明该案例的架构和生成的 SQL http://sqlfiddle.com/#!9/2f614f/3 在 cmets 中有违规行/语句。

    create table TOUR_RESULT (
      player_id int,
      nsp_score int
    );

    insert into TOUR_RESULT values (1,4);
    insert into TOUR_RESULT values (1,14);
    insert into TOUR_RESULT values (1,24);
    insert into TOUR_RESULT values (1,34);
    insert into TOUR_RESULT values (1,44);
    insert into TOUR_RESULT values (2,3);
    insert into TOUR_RESULT values (2,13);
    insert into TOUR_RESULT values (2,23);
    insert into TOUR_RESULT values (2,33);
    insert into TOUR_RESULT values (2,43);
    insert into TOUR_RESULT values (3,3);
    insert into TOUR_RESULT values (3,13);
    insert into TOUR_RESULT values (4,130);
    insert into TOUR_RESULT values (5,2);
    insert into TOUR_RESULT values (5,7);
    insert into TOUR_RESULT values (5,7);
    insert into TOUR_RESULT values (5,7);
    insert into TOUR_RESULT values (5,5);
    insert into TOUR_RESULT values (5,7);
    insert into TOUR_RESULT values (5,10);
    insert into TOUR_RESULT values (5,12);

    SELECT `tr1`.`player_id`, sum(`tr1`.`nsp_score`) AS `score`
      FROM `tour_result` AS `tr1`
      WHERE 3 >=
    --    (SELECT `tr3`.`count`
    --     FROM
     (SELECT count(*) AS `count`
       FROM `tour_result` AS `tr2`
       WHERE (`tr1`.`player_id` = `tr2`.`player_id`
           AND `tr1`.`nsp_score` <= `tr2`.`nsp_score`)) 
    --               AS `tr3`)
    GROUP BY `tr1`.`player_id`
    ORDER BY 2 desc;

取消注释行时的错误是

    Unknown column 'tr1.player_id' in 'where clause'

【问题讨论】:

  • "best 3 results" 对于一个玩家来说似乎是三个最低的 nsp_score 值。当玩家的 nsp_score 值重复时会发生什么,例如,当玩家的 nsp_scores 为 (2),(7),(7),(7),(7),(10),(12) 时,结果应该是什么? (即,让我们确保我们提出的查询满足规范......)
  • 好收获。我已经更新了场景。看似微不足道的任务的兔子洞总是令人惊叹......
  • ORDER BY 2 DESC 对应于 jOOQ 中的DSL.inline(2).desc()...

标签: mysql sql scala jooq


【解决方案1】:

很遗憾,MariaDB 和 MySQL 不允许在相关子查询中引用“向上两层”的列。但如果你有 MariaDB 10.2 或 MySQL 8.0,你可以使用 window functions 来完成这项工作:

SQL 版本

SELECT tr1.player_id, SUM(nsp_score) AS score
FROM (
  SELECT 
    tr2.player_id, 
    tr2.nsp_score, 
    ROW_NUMBER () OVER (PARTITION BY tr2.player_id ORDER BY tr2.nsp_score DESC) rn
  FROM tour_result AS tr2
) AS tr1
WHERE rn <= 3
GROUP BY tr1.player_id;

ROW_NUMBER() 过滤将在分数中准确选择 3 个获胜行。如果您想拥有 3 行或更多行,并且它们被绑定(WITH TIES 语义),您可以使用RANK()I've also blogged about this topic in the past.

jOOQ 版本

这转换为以下 jOOQ 查询:

val tr1 = TOUR_RESULT.as("tr1")
val tr2 = TOUR_RESULT.as("tr2")
val result = sql
    .select(tr1.PLAYER_ID, sum(tr1.NSP_SCORE).as("score"))
    .from(table(
       select(
         tr2.PLAYER_ID,
         tr2.NSP_SCORE,
         rowNumber().over(
           partitionBy(tr2.PLAYER_ID)
          .orderBy(tr2.NSP_SCORE.desc())).as("rn"))
      .from(tr2)
    ).as(tr1))
    .where(field(name("rn")).le(inline(3)))
    .groupBy(tr1.PLAYER_ID)
    .fetch()

以上假设导入

import org.jooq.impl.DSL._

【讨论】:

    猜你喜欢
    • 2017-08-11
    • 2016-05-22
    • 2019-12-04
    • 2020-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-18
    • 2017-04-14
    相关资源
    最近更新 更多