我正在针对 PostgreSQL 数据库使用 jOOQ 。
我想在jsonb_object_agg(name, value)
的结果集中使用LEFT OUTER JOIN
。
联接是一个OUTER
联接,有时聚合功能的name
组件只是null
:这是行不通的。然后我会去:
COALESCE(
json_object_agg(table.name, table.value) FILTER (WHERE table.name IS NOT NULL),
'{}'
)::json
到目前为止,我用来调用jsonb_object_agg
的代码如下(不完全是,但归结为):
public static Field<?> jsonbObjectAgg(final Field<?> key, final Select<?> select) {
return DSL.field("jsonb_object_agg({0}, ({1}))::jsonb", JSON_TYPE, key, select);
}
...,其中JSON_TYPE
是:
private static final DataType<JsonNode> JSON_TYPE = SQLDataType.VARCHAR.asConvertedDataType(/* a custom Converter */);
我很想利用 jOOQ 的AggregateFilterStep
界面,尤其是能够使用其AggregateFilterStep#filterWhere(Condition... conditions)
。
但是,org.jooq.impl.Function
(通过implements AggregateFilterStep
和AgregateFunction
间接访问的ArrayAggOrderByStep
类的可见性受限于其package
,所以我不能只是盲目地回收DSL#ArrayAggOrderByStep
的实现:
public static <T> ArrayAggOrderByStep<T[]> arrayAgg(Field<T> field) {
return new org.jooq.impl.Function<T[]>(Term.ARRAY_AGG, field.getDataType().getArrayDataType(), nullSafe(field));
}
我最接近合理的方法是...构建自己的coalesceAggregation
函数,该函数专门合并聚合的字段:
// Can't quite use AggregateFunction there
// v v
public static <T> Field<T> coalesceAggregation(final Field<T> agg, final Condition coalesceWhen, @NonNull final T coalesceTo) {
return DSL.coalesce(DSL.field("{0} FILTER (WHERE {1})", agg.getType(), agg, coalesceWhen), coalesceTo);
}
public static <T> Field<T> coalesceAggregation(final Field<T> agg, @NonNull final T coalesceTo) {
return coalesceAggregation(agg, agg.isNotNull(), coalesceTo);
}
...但是我随后遇到了T
类型为JsonNode
的问题,其中DSL#coalesce
似乎CAST
我的coalesceTo
到{{1 }}。
或者,您知道:
varchar
但这是最后的选择:感觉就像离让用户将他们想要的任何SQL注入我的数据库仅一步之遥
在 jOOQ 中,有没有一种方法可以“正确地”实现自己的聚合函数,如实际的DSL.field("COALESCE(jsonb_object_agg({0}, ({1})) FILTER (WHERE {0} IS NOT NULL), '{}')::jsonb", JSON_TYPE, key, select)
?
我想尽可能避免由org.jooq.AgregateFunction
生成它(不是我不喜欢它-只是我们的管道很恐怖)。
答案 0 :(得分:1)
jOOQ DSL
API中缺少一项功能,即创建plain SQL aggregate functions。之所以无法使用该功能(从jOOQ 3.11开始)是因为要指定支持所有特定于供应商的所有选项的供应商不可知聚合函数有很多微妙的内部因素,
FILTER (WHERE ...)
子句(如您在问题中所述),必须使用CASE
OVER (...)
子句将聚合函数转换为窗口函数WITHIN GROUP (ORDER BY ...)
子句支持有序集合聚合函数DISTINCT
子句(受支持的地方)在您的特定情况下,一种简单的解决方法是也使用plain SQL templating,就像您在问题中提到的那样:
DSL.field("COALESCE(jsonb_object_agg({0}, ({1})) FILTER (WHERE {0} IS NOT NULL), '{}')::jsonb", JSON_TYPE, key, select)
或者您做您之前提到的事情。关于这个问题:
...但是后来我遇到了T型为JsonNode的问题,在这里DSL#coalesce似乎将我的coalesceTo转换为varchar。
那可能是因为您使用的是agg.getType()
返回了Class<?>
,而不是agg.getDataType()
返回了DataType<?>
。
但这将是最后的手段:感觉就像离让用户将他们想要的任何SQL注入我的数据库仅一步之遥
我不确定为什么这是一个问题。您仍然可以自己控制普通SQL API的使用,并且用户也无法控制key
和select
中的内容,因为您也可以控制这些元素。