我尝试以一对多的关系实现一侧的偏移分页,并通过多侧过滤并通过窗口函数计算总数。
由于笛卡尔积问题,我得到了指定球颜色过滤器值可用的不正确的框总数:
Map<Integer, List<Box>> result = ctx.selectDistinct(
Box.asterisk(), count().over().as("total"))
.from(BOX)
.leftJoin(BALL).on(BALL.BOX_ID.eq(BOX.ID))
.where(BALL.COLOR.eq("red"))
.orderBy(BOX.BOX_ID)
.limit(size)
.offset(size * page)
.fetchGroups(field("total", Integer.class), record-{ mapping logic in dto });
我尝试使用 countDistinct 将 JOOQ 查询重写为:
Map<Integer, List<Box>> result = ctx.selectDistinct(
Box.asterisk(), countDistinct(BOX.ID).over().as("total"))
.from(BOX)
.leftJoin(BALL).on(BALL.BOX_ID.eq(BOX.ID))
.where(BALL.COLOR.eq("red"))
.orderBy(BOX.BOX_ID)
.limit(size)
.offset(size * page)
.fetchGroups(field("total", Integer.class), record-{ mapping logic in projection});
并得到:org.postgresql.util.PSQLException:错误:窗口函数未实现DISTINCT
我该如何解决这个问题?我不想编写两个单独的 JOOQ 查询来获取总计数并获取页面。
我想消除单个JOOQ查询结果集中的重复项,并使其通过数据库端的一一选择以最有效的方式计算出正确的全局可用红球框记录数量。
您仅投影
BOX
列,仅使用 BALL
连接来计算每个盒子中的红球。因此,只需使用 GROUP BY
(知道 DISTINCT
的大多数用法可能不是最佳选择):
Map<Integer, List<Box>> result =
ctx.select(BOX.fields())
.select(count(BALL.ID).as("total"))
.from(BOX)
.leftJoin(BALL)
.on(BALL.BOX_ID.eq(BOX.ID))
.and(BALL.COLOR.eq("red"))
.groupBy(BOX.ID)
.orderBy(BOX.ID)
.limit(size)
.offset(size * page)
.fetchGroups(field("total", Integer.class), record-{ mapping logic in dto });
几点说明:
GROUP BY
主键,并且仍然投影所有功能相关列。 我已经在这里写过博客了。如果您不使用 PostgreSQL,那么只需在 BOX.fields()
中列出所有 GROUP BY
即可。BALL.COLOR.eq("red")
谓词移至 LEFT JOIN
的 ON
子句中,另请参阅我写的解释它的博客文章。简而言之,如果您将该谓词留在 WHERE
子句中,则会将 LEFT JOIN
变成 INNER JOIN
,这是您不想要的。请注意,在这种情况下,您不能再 COUNT(*)
了,但是 COUNT(BALL.ID)
可以在没有 0
的情况下获得 BOX
的 BALL
计数(而不是 1
计数)。