这里的问题是SQL's logical order of operations.对结果进行计数的任何手段(合计计数和窗口计数)都发生before,应用LIMIT
.因此,如果您不希望LIMIT
对您的计数产生任何影响,则必须以它首先计数的方式编写查询.但是,如果从WHERE
子句中没有得到任何结果,那么在使用窗口计数时就不会得到任何行.当使用聚合计数时,您会得到一行,但是您不能再这么容易地应用LIMIT
.(如果没有LIMIT
,只需使用Aggregate Count和multisetAgg()
即可).
因此,我可以看到两种解决方案,一种缓慢但简单,一种更快但更复杂:
执行查询中的所有操作,访问表两次:
如果您想要将所有映射逻辑移到查询本身中,我怀疑您必须对PERSON
个表运行2个查询,出于性能原因,这可能不是我们所希望的:
ctx.select(
count().filterWhere(PERSON.NAME.startsWith("A")).convertFrom(Info::new),
multiset(
select(PERSON_ID, PERSON_NAME)
.from(PERSON)
.where(PERSON.NAME.startsWith("A"))
.orderBy(PERSON.NAME.asc())
.limit(5)
).convertFrom(r -> r.map(mapping(Person::new)))
)
.from(PERSON)
.fetchOne(mapping(MyResult::new))
这适用于您的简单示例,但是一旦开始连接事物,您就必须在内部查询和外部查询之间复制大量查询逻辑,这并不是真正需要的.
Using JDK Collector
API
但是您不必像您的目标数据 struct 那样以分层的方式形成查询.在jOOQ之外,您仍然可以在客户端执行some个映射逻辑,例如:
ctx.select(
count().over().convertFrom(Info::new),
row(PERSON_ID, PERSON_NAME).mapping(Person::new)
)
.from(PERSON)
.where(PERSON.NAME.startsWith("A"))
.orderBy(PERSON.NAME.asc())
.limit(5)
.collect(collectingAndThen(
groupingBy(
// There's a single possible value from count().over()
r -> r.value1(),
mapping(r -> r.value2(), toList())
),
m -> {
// Probably, just use var here...
Iterator<Entry<Info, List<Person>>> it = m.entrySet().iterator();
if (it.hasNext()) {
Entry<Info, List<Person>> e = it.next();
return new MyResult(e.getKey(), e.getValue());
}
else
return new MyResult(new Info(0), Collections.emptyList());
}
)));
这假设有以下静态导入:
import static java.util.stream.Collectors.*;
诚然,它并不是那么漂亮.您可以将其重构为不同的格式,或者为这种结果编写一些更通用的实用程序,但我希望您能领会其中的精髓.
Note:如果您翻页到最后一页之外,这种方法不会给出正确的计数值