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<?>
,其中列类型是通用通配符。这里有一些解决方案:
- 如果不需要,请避免使用类型安全。您可以随时声明您的 table
Table<?> req_max
。仅从您的示例来看,我不确定您是否需要任何类型安全
将您的字段引用提取为局部变量。而不是嵌入例如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);
我试图在 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<?>
,其中列类型是通用通配符。这里有一些解决方案:
- 如果不需要,请避免使用类型安全。您可以随时声明您的 table
Table<?> req_max
。仅从您的示例来看,我不确定您是否需要任何类型安全 将您的字段引用提取为局部变量。而不是嵌入例如
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);