对于经典排名问题(每位玩家最好的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一起使用,并按降序的“分数”(第2列)进行排序.
我相信我可以使用原始SQL语句重写整个语句并强制转换结果(因此绕过jOOQ API).但是,我希望尽可能长的时间保持编译器和jOOQ API的安全.因此,如果我不得不再次触摸此查询,也许将来有一种解决方案仍然可读.
可以在下面找到说明案例的模式和生成的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'
解决方法:
不幸的是,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行或更多行(如果已绑定)(具有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._