在 JOOQ 分页之前获取总行数?
Get total rowcount before paginating in JOOQ?
我很想知道在使用 OFFSET
和 LIMIT
分页之前是否有更好的方法从 JOOQ 查询中获取总行数
我们尝试使用 JOOQ 的合成 SEEK 子句,但是因为我们的 ID 是无序的 UUID,所以它不起作用。
我们当前的实现是执行查询两次,第一次是在我们设置偏移量和限制以获取 result/row 计数之前。
然后我们在第二个查询中得到结果。
SelectQuery<Record> select = context.selectQuery();
select.addSelect(FOO_TABLE.fields());
select.addFrom(FOO_TABLE);
int totalElements = select.fetch().size();
select.addOffset(20);
select.addLimit(50));
List<Foo> paginatedFoo = select.fetchInto(Foo.class);
这只是必须接受这个 implementation/design 的东西,还是有任何 JDBC 驱动魔法可以消除在数据库上执行两个查询的需要?
欢迎任何意见或建议!
We tried using JOOQs synthetic SEEK clause however since our IDs are unordered UUIDs it does not work.
分页时,您按对用户有意义的内容进行排序。因此,您可能会先订购/查找其他一些列(例如一些 DATE
列),然后仅使用 UUID
以获得明确、稳定的结果。我不明白为什么 SEEK
对你不起作用。
如果使用 SEEK
(keyset pagination) 对您的应用程序有意义,从逻辑上讲,出于性能原因,它更可取:
- 你不用数,因为没关系
- You don't have to use offset
Is this just something that has to be accepted with this implementation/design, or is there any JDBC driver magic that would eliminate the need for two queries to be executed on the DB?
数据库需要做很多额外的工作。特别是如果你这样做的话:
// Don't do this!
int totalElements = select.fetch().size();
您正在传输整个数据集!如果您必须计算单独查询中的行数,至少运行该查询完全在数据库中:
// Do this instead (prior to adding the limit):
context.fetchCount(select);
但为什么不直接使用 window 函数呢?将 DSL.count().over()
添加到您的查询以计算如果您没有分页,您的查询将产生的总行数,并且您已准备就绪。
之所以可以为此使用 window 函数,是因为它们在所有其他操作(WHERE
、GROUP BY
、HAVING
等之后计算起来很方便.),但在分页之前 (OFFSET
, LIMIT
)。 See this article about the logical order of operations.
下面给你举个例子,应该是你要找的。
Result<Record> result = dslContext.selectDistinct(
REQUEST.ID,
count().over().as(TOTAL_ELEMENTS))
.from(REQUEST)
.groupBy(REQUEST.ID)
.orderBy(REQUEST.CREATED_DATE.desc())
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
现在让我们求出总元素数。
int totalElements = 0;
Optional<?> optTotalElements = result.getValues(TOTAL_ELEMENTS).stream().findFirst();
if(optTotalElements.isPresent()) {
totalElements = Integer.parseInt(optTotalElements.get().toString());
}
double totalPages = (double) totalElements / pageable.getPageSize();
我很想知道在使用 OFFSET
和 LIMIT
我们尝试使用 JOOQ 的合成 SEEK 子句,但是因为我们的 ID 是无序的 UUID,所以它不起作用。
我们当前的实现是执行查询两次,第一次是在我们设置偏移量和限制以获取 result/row 计数之前。
然后我们在第二个查询中得到结果。
SelectQuery<Record> select = context.selectQuery();
select.addSelect(FOO_TABLE.fields());
select.addFrom(FOO_TABLE);
int totalElements = select.fetch().size();
select.addOffset(20);
select.addLimit(50));
List<Foo> paginatedFoo = select.fetchInto(Foo.class);
这只是必须接受这个 implementation/design 的东西,还是有任何 JDBC 驱动魔法可以消除在数据库上执行两个查询的需要?
欢迎任何意见或建议!
We tried using JOOQs synthetic SEEK clause however since our IDs are unordered UUIDs it does not work.
分页时,您按对用户有意义的内容进行排序。因此,您可能会先订购/查找其他一些列(例如一些 DATE
列),然后仅使用 UUID
以获得明确、稳定的结果。我不明白为什么 SEEK
对你不起作用。
如果使用 SEEK
(keyset pagination) 对您的应用程序有意义,从逻辑上讲,出于性能原因,它更可取:
- 你不用数,因为没关系
- You don't have to use offset
Is this just something that has to be accepted with this implementation/design, or is there any JDBC driver magic that would eliminate the need for two queries to be executed on the DB?
数据库需要做很多额外的工作。特别是如果你这样做的话:
// Don't do this!
int totalElements = select.fetch().size();
您正在传输整个数据集!如果您必须计算单独查询中的行数,至少运行该查询完全在数据库中:
// Do this instead (prior to adding the limit):
context.fetchCount(select);
但为什么不直接使用 window 函数呢?将 DSL.count().over()
添加到您的查询以计算如果您没有分页,您的查询将产生的总行数,并且您已准备就绪。
之所以可以为此使用 window 函数,是因为它们在所有其他操作(WHERE
、GROUP BY
、HAVING
等之后计算起来很方便.),但在分页之前 (OFFSET
, LIMIT
)。 See this article about the logical order of operations.
下面给你举个例子,应该是你要找的。
Result<Record> result = dslContext.selectDistinct(
REQUEST.ID,
count().over().as(TOTAL_ELEMENTS))
.from(REQUEST)
.groupBy(REQUEST.ID)
.orderBy(REQUEST.CREATED_DATE.desc())
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
现在让我们求出总元素数。
int totalElements = 0;
Optional<?> optTotalElements = result.getValues(TOTAL_ELEMENTS).stream().findFirst();
if(optTotalElements.isPresent()) {
totalElements = Integer.parseInt(optTotalElements.get().toString());
}
double totalPages = (double) totalElements / pageable.getPageSize();