SQL, сгенерированный jOOQ для вложенного выбора, не работает в MySQL / MariaDB - PullRequest
0 голосов
/ 02 мая 2018

Для классической задачи ранжирования (сумма лучших 3 результатов на игрока) я придумал следующий подзапрос, используя jOOQ DSL и Scala (для встраивания как выберите значение и включите в более сложный запрос):

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 и сортировать по убыванию «Score» (столбец 2). Я уверен, что смогу переписать весь оператор, используя необработанный оператор SQL и приведя результат (следовательно, в обход API jOOQ). Однако я хотел бы оставаться чистым с компилятором и API jOOQ как моим пастухом как можно дольше. Так что, возможно, есть решение, которое будет доступно для чтения в будущем, если мне когда-нибудь придется снова коснуться этого запроса.

Схему и сгенерированный SQL, иллюстрирующий случай, можно найти в http://sqlfiddle.com/#!9/2f614f/3 с оскорбительными строками / высказываниями в комментариях.

    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'

1 Ответ

0 голосов
/ 04 мая 2018

К сожалению, MariaDB и MySQL не позволяют ссылаться на столбцы «на два уровня выше» в ваших коррелированных подзапросах. Но если у вас есть MariaDB 10.2 или MySQL 8.0, вы можете использовать оконные функции для работы:

Версия 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(). Я также писал в блоге об этой теме в прошлом .

Версия 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._
...