JOOQ派生表

JOOQ Derived Tables

我试图在 JOOQ 中表达以下 SQL。但是,我要么在使用派生表的类型方面遇到大量问题,要么我得到了一些可以编译但在 SQL 级别甚至 JAVA 级别失败的东西。谁能告诉我如何在这种情况下正确使用派生表?

SELECT
    id,
    ROUND(num_realized / num_requirements, 2) AS realized_percent,
    ROUND(num_requirements / max_req, 2) AS activity_percent
FROM (
    SELECT
        requirement.project_id AS id,
        COUNT(requirement.id) AS num_requirements,
        COUNT(requirement.realized) AS num_realized
    FROM
        requirement
    GROUP BY
        requirement.project_id) AS stats
    CROSS JOIN (
        SELECT
            MAX(num_requirements) AS max_req
        FROM (
            SELECT
                requirement.project_id AS id,
                COUNT(requirement.id) AS num_requirements,
                COUNT(requirement.realized) AS num_realized
            FROM
                requirement
            GROUP BY
                requirement.project_id) AS stats) AS req_max 

该语句在 SQL 中应用时工作正常,但我无法将此表达式放入 JOOQ。

我最近的尝试是使用

Table<Record3<Integer, Integer, Integer>> stats =
DSL.select(
    REQUIREMENT.PROJECT_ID.as("id"),
    DSL.count(REQUIREMENT.ID).as("num_requirements"),
    DSL.count(REQUIREMENT.REALIZED).as("num_realized")
).from(REQUIREMENT).groupBy(REQUIREMENT.PROJECT_ID).asTable("stats");

Table<Record2<Integer, Integer>> req_max =
    DSL.select(
        stats.field(0),
        DSL.min(stats.field(1))
    )
    .from(stats).asTable("req_max");

但是我收到错误:不兼容的类型:

Table<Record2<CAP#1,CAP#2>> cannot be converted to Table<Record2<Integer,Integer>>

我尝试了很多不同的技术,包括定义数据类型和使用 .field(String, Datatype) 而不是使用 "Records" 但无论我在做什么,它要么不编译,要么在执行时失败未知错误。

我很乐意提供任何帮助。

改用 window 函数

一般来说,应该尽可能避免自连接。在许多情况下,window 函数可以比嵌套查询中的聚合更优雅地解决问题。如果您使用 MySQL 8,您的查询可以重写为:

SELECT
    requirement.project_id AS id,
    ROUND(COUNT(requirement.realized) / COUNT(requirement.id), 2) AS realized_percent,
    ROUND(COUNT(requirement.id) / MAX(COUNT(requirement.id)) OVER(), 2) AS activity_percent
FROM
    requirement
GROUP BY
    requirement.project_id    

注意 MAX(..) OVER() window function, which can aggregate ordinary aggregation functions as explained here。我知道您正在使用 MySQL 5.7,它还没有 window 功能支持,但是为了完整起见,这个答案需要一个基于 window 功能的解决方案——也许是升级的动力:-)

许多复杂的 jOOQ 查询可以通过首先使基础 SQL 查询更简单来变得更简单。

导出的table问题你运行变成了

问题是您对 stats.field(0)stats.field(1) 的使用。方法签名是

Field<?> field​(int index)

当您通过索引访问 table 的列时,jOOQ 无法为您提供类型安全,因此 return 类型是 Field<?>,其中列类型是通用通配符。这里有一些解决方案:

  1. 如果不需要,请避免使用类型安全。您可以随时声明您的 table Table<?> req_max。仅从您的示例来看,我不确定您是否需要任何类型安全
  2. 将您的字段引用提取为局部变量。而不是嵌入例如stats table 中的 id 列,为什么不呢:

    Field<Integer> id = REQUIREMENT.PROJECT_ID.as("id");
    Field<Integer> numRequirements = DSL.count(REQUIREMENT.ID).as("num_requirements");
    Field<Integer> numRealized = DSL.count(REQUIREMENT.REALIZED).as("num_realized");
    

    然后像这样使用它:

    var stats =
    DSL.select(id, numRequirements, numRealized)
       .from(REQUIREMENT)
       .groupBy(REQUIREMENT.PROJECT_ID)
       .asTable("stats");
    
    var reqMax =
    DSL.select(stats.field(id), DSL.max(stats.field(numRequirements)))
       .from(stats)
       .asTable(reqMax);