英文:
JOOQ multiple select count in one connection with PostgreSQL
问题
我有一个名为 SUBSCRIPTION
的表,我想在一个连接中使用不同的谓词运行多个使用 JOOQ 编写的 selectCount
查询到数据库。为此,我已经创建了一个查询列表:
List<Query> countQueries = channels.stream().map(c ->
selectCount().from(SUBSCRIPTION)
.innerJoin(SENDER).on(SENDER.ID.equal(SUBSCRIPTION.SENDER_ID))
.innerJoin(CHANNEL).on(CHANNEL.ID.equal(SUBSCRIPTION.CHANNEL_ID))
.where(SENDER.CODE.equal(senderCode))
.and(CHANNEL.CODE.equal(c))
).collect(toList());
最后,我使用 batch
方法来执行这个查询列表:
using(configuration).batch(countQueries).execute();
我预期在 execute
方法的返回值中获得上述查询的结果,但我得到了一个填充有 0
值的整数数组。
这种方法是使用 JOOQ 运行多个 selectCount
的正确方式吗?
execute
方法返回的整数数组有什么意义?
我已经查看了这个链接,在 JOOQ 的博客中讨论了“如何在单个查询中计算多个聚合函数”,但它只涉及到 SQL 查询,没有涉及 JOOQ 方言。
英文:
I have a table SUBSCRIPTION
, I want to run multiple selectCount
written with JOOQ in one connection with different predicates to the database.
To do so, I have created a list of queries:
List<Query> countQueries = channels.stream().map(c ->
selectCount().from(SUBSCRIPTION)
.innerJoin(SENDER).on(SENDER.ID.equal(SUBSCRIPTION.SENDER_ID))
.innerJoin(CHANNEL).on(CHANNEL.ID.equal(SUBSCRIPTION.CHANNEL_ID))
.where(SENDER.CODE.equal(senderCode))
.and(CHANNEL.CODE.equal(c))
).collect(toList());
And finally, I have launched this list of queries using batch
:
using(configuration).batch(countQueries).execute();
I have expected to have the results of the above queries in the return values of execute
, but I get an array of integer filled with 0
values.
Is this the right way to run multiple selectCount
using JOOQ?
What is the signification of the integer array returned by the execute
method?
I have checked this link, in the JOOQ blog, talking about "How to Calculate Multiple Aggregate Functions in a Single Query", but It's just about SQL queries, no JOOQ dialects.
答案1
得分: 1
对您的假设的评论
> 我原以为上述查询的结果会在execute的返回值中,但实际上我得到的是一个填充有0值的整数数组。
batch()
API 只能用于 DML 查询(INSERT
,UPDATE
,DELETE
),就像使用原生的 JDBC 一样。我是说,你可以将查询作为批处理运行,但不能以这种方式获取结果。
> 我查看了这个 链接,在 JOOQ 的博客中谈到了“如何在单个查询中计算多个聚合函数”,但只涉及 SQL 查询,没有涉及 JOOQ 方言。
通常情况下,纯粹的 SQL 查询几乎可以直接翻译成 jOOQ,所以你可以在你的情况下应用该文章中的技巧。实际上,你应该这样做!运行这么多查询绝对不是一个好主意。
将链接的查询翻译为 jOOQ
因此,让我们看看如何将链接中的纯SQL示例翻译为你的情况:
Record record =
ctx.select(
channels.stream()
.map(c -> count().filterWhere(CHANNEL.CODE.equal(c)).as(c))
.collect(toList())
)
.from(SUBSCRIPTION)
.innerJoin(SENDER).on(SENDER.ID.equal(SUBSCRIPTION.SENDER_ID))
.innerJoin(CHANNEL).on(CHANNEL.ID.equal(SUBSCRIPTION.CHANNEL_ID))
.where(SENDER.CODE.equal(senderCode))
.and(CHANNEL.CODE.in(channels)) // 不是严格必要的,但可能会加快速度
.fetch();
这将生成包含所有计数值的单个记录。
和往常一样,这假设了以下的静态导入:
import static org.jooq.impl.DSL.*;
使用经典的 GROUP BY
当然,在你的特定情况下,你也可以使用经典的 GROUP BY
。这可能会更快一些:
Result<?> result =
ctx.select(CHANNEL.CODE, count())
.from(SUBSCRIPTION)
.innerJoin(SENDER).on(SENDER.ID.equal(SUBSCRIPTION.SENDER_ID))
.innerJoin(CHANNEL).on(CHANNEL.ID.equal(SUBSCRIPTION.CHANNEL_ID))
.where(SENDER.CODE.equal(senderCode))
.and(CHANNEL.CODE.in(channels)) // 这次你需要过滤
.groupBy(CHANNEL.CODE)
.fetchOne();
这会生成一个包含每个代码的计数值的表。或者,将其取出到一个 Map<String, Integer>
中:
Map<String, Integer> map =
ctx.select(CHANNEL.CODE, count())
.from(SUBSCRIPTION)
.innerJoin(SENDER).on(SENDER.ID.equal(SUBSCRIPTION.SENDER_ID))
.innerJoin(CHANNEL).on(CHANNEL.ID.equal(SUBSCRIPTION.CHANNEL_ID))
.where(SENDER.CODE.equal(senderCode))
.and(CHANNEL.CODE.in(channels))
.groupBy(CHANNEL.CODE)
.fetchMap(CHANNEL.CODE, count());
英文:
Comments on your assumptions
> I have expected to have the results of the above queries in the return values of execute, but I get an array of integer filled with 0 values.
The batch()
API can only be used for DML queries (INSERT
, UPDATE
, DELETE
), just like with native JDBC. I mean, you can run the queries as a batch, but you cannot fetch the results this way.
> I have checked this link, in the JOOQ blog, talking about "How to Calculate Multiple Aggregate Functions in a Single Query", but It's just about SQL queries, no JOOQ dialects.
Plain SQL queries almost always translate quite literally to jOOQ, so you can apply the technique from that article also in your case. In fact, you should! Running so many queries is definitely not a good idea.
Translating that linked query to jOOQ
So, let's look at how to translate that plain SQL example from the link to your case:
Record record =
ctx.select(
channels.stream()
.map(c -> count().filterWhere(CHANNEL.CODE.equal(c)).as(c))
.collect(toList())
)
.from(SUBSCRIPTION)
.innerJoin(SENDER).on(SENDER.ID.equal(SUBSCRIPTION.SENDER_ID))
.innerJoin(CHANNEL).on(CHANNEL.ID.equal(SUBSCRIPTION.CHANNEL_ID))
.where(SENDER.CODE.equal(senderCode))
.and(CHANNEL.CODE.in(channels)) // Not strictly necessary, but might speed up things
.fetch();
This will produce a single record containing all the count values.
As always, this is assuming the following static import
import static org.jooq.impl.DSL.*;
Using classic GROUP BY
Of course, you can also just use a classic GROUP BY
in your particular case. This might even be a bit faster:
Result<?> result =
ctx.select(CHANNEL.CODE, count())
.from(SUBSCRIPTION)
.innerJoin(SENDER).on(SENDER.ID.equal(SUBSCRIPTION.SENDER_ID))
.innerJoin(CHANNEL).on(CHANNEL.ID.equal(SUBSCRIPTION.CHANNEL_ID))
.where(SENDER.CODE.equal(senderCode))
.and(CHANNEL.CODE.in(channels)) // This time, you need to filter
.groupBy(CHANNEL.CODE)
.fetchOne();
This now produces a table with one count value per code. Alternatively, fetch this into a Map<String, Integer>
:
Map<String, Integer> map =
ctx.select(CHANNEL.CODE, count())
.from(SUBSCRIPTION)
.innerJoin(SENDER).on(SENDER.ID.equal(SUBSCRIPTION.SENDER_ID))
.innerJoin(CHANNEL).on(CHANNEL.ID.equal(SUBSCRIPTION.CHANNEL_ID))
.where(SENDER.CODE.equal(senderCode))
.and(CHANNEL.CODE.in(channels))
.groupBy(CHANNEL.CODE)
.fetchMap(CHANNEL.CODE, count());
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论