在 JOOQ 分页之前获取总行数?

Get total rowcount before paginating in JOOQ?

我很想知道在使用 OFFSETLIMIT

分页之前是否有更好的方法从 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) 对您的应用程序有意义,从逻辑上讲,出于性能原因,它更可取:

  1. 你不用数,因为没关系
  2. 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 函数,是因为它们在所有其他操作(WHEREGROUP BYHAVING 等之后计算起来很方便.),但在分页之前 (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();