【问题标题】:Using unnest as a field rather than a table in jOOQ在 jOOQ 中使用 unnest 作为字段而不是表
【发布时间】:2016-08-03 00:19:47
【问题描述】:

这是我试图在 PostgreSQL 中运行的查询:

SELECT * FROM message WHERE id IN (
    SELECT unnest(message_ids) "mid"
        FROM session_messages WHERE session_id = '?' ORDER BY "mid" ASC
);

但是,我无能为力:

create.selectFrom(Tables.MESSAGE).where(Tables.MESSAGE.ID.in(
    create.select(DSL.unnest(..))

因为DSL.unnestTable<?>,这是有道理的,因为它试图采用List 类似的对象(主要是文字)并将其转换为表格。

我觉得我需要找到一种方法来将函数包装在我的字段名称周围,但我不知道如何继续。

注意。字段message_ids 的类型为bigint[]

编辑

所以,这就是我现在的做法,而且效果完全符合预期,但我不确定这是否是最好的做法:

Field<Long> unnestMessageIdField = DSL.field(
                "unnest(" +  SESSION_MESSAGES.MESSAGE_IDS.getName() + ")",
                Long.class)
        .as("mid");

Field<Long> messageIdField = DSL.field("mid", Long.class);

MESSAGE.ID.in(
        ctx.select(messageIdField).from(
            ctx.select(unnestMessageIdField)
               .from(Tables.CHAT_SESSION_MESSAGES)
                    .where(Tables.CHAT_SESSION_MESSAGES.SESSION_ID.eq(sessionId))
            )
            .where(condition)
)

EDIT2

查看https://github.com/jOOQ/jOOQ/blob/master/jOOQ/src/main/java/org/jooq/impl/DSL.java 上的代码后,我想正确的做法是:

DSL.function("unnest", SQLDataTypes.BIGINT.getArrayType(), SESSION_MESSAGES.MESSAGE_IDS)

EDIT3

由于 lukas 一如既往地为我的 jOOQ 困境而来,我将利用这一点 :)

试图概括这个函数,在 sort 的签名中

public <T> Field<T> unnest(Field<T[]> arrayField) {
    return DSL.function("unnest", <??>, arrayField);
}

我不知道如何获取数据类型。似乎有一种方法可以使用DataType::getArrayDataType()DataType&lt;T&gt; 获取DataType&lt;T[]&gt;,但反过来是不可能的。我找到了ArrayDataType 这个类,但它似乎是包私有的,所以我不能使用它(即使我可以,它也不会暴露字段elementType)。

【问题讨论】:

  • 你使用的是哪个 PostgreSQL 版本?
  • 目前使用的是 9.4(另外,请您看看 EDIT3,好吗?)

标签: postgresql jooq


【解决方案1】:

旧的 PostgreSQL 版本有一个时髦的想法,即可以从 SELECT 子句中生成一个表,并将其扩展为“外部”表,就好像它是在 FROM 子句中声明的一样。这是一个非常模糊的 PostgreSQL 遗留问题,这个例子是摆脱它的好机会,并使用 LATERAL 代替。您的查询相当于这个:

SELECT * 
FROM message 
WHERE id IN (
    SELECT "mid"
    FROM session_messages 
    CROSS JOIN LATERAL unnest(message_ids) AS t("mid")
    WHERE session_id = '?' 
);

这可以更容易地转换为 jOOQ:

DSL.using(configuration)
   .select()
   .from(MESSAGE)
   .where(MESSAGE.ID).in(
        select(field(name("mid"), MESSAGE.ID.getDataType()))
       .from(SESSION_MESSAGES)
       .crossJoin(lateral(unnest(SESSION_MESSAGES.MESSAGE_IDS)).as("t", "mid"))
       .where(SESSION_MESSAGES.SESSION_ID.eq("'?'"))
   )

【讨论】:

  • 除了您的解决方案在结构上看起来更好之外,这两种方法之间是否存在性能差异?和往常一样,非常感谢您抽出宝贵的时间。
  • 有趣的是,您的方法似乎产生了更简单的执行计划。你必须测量(或问另一个问题!)
【解决方案2】:

问题中的Edit3 非常接近于解决这个问题。 我们可以为 jOOQ 创建一个自定义的通用 unnest 方法,它接受 Field 并在 jOOQ 查询中正常使用它。

辅助方法:

public static <T> Field<T> unnest(Field<T[]> field, Class<T> type) {
    return DSL.function("unnest", type, field);
}

用法:

public void query(SessionId sessionId) {

    var field = unnest(SESSION_MESSAGES.MESSAGE_IDS, UUID.class);

    dsl.select().from(MESSAGE).where(
            MESSAGE.ID.in(
                dsl.select(field).from(SESSION_MESSAGES)
                .where(SESSION_MESSAGES.SESSION_ID.eq(sessionId.id))
                .orderBy(field)
            )
    );

}

【讨论】:

  • 您不必传递 type 参数,您可以直接从 field.getType().getComponentType() 获取它(使用不安全的演员表)
猜你喜欢
  • 1970-01-01
  • 2020-12-02
  • 2016-06-03
  • 2020-01-24
  • 2018-03-30
  • 1970-01-01
  • 2021-06-01
  • 2013-11-19
  • 2014-02-02
相关资源
最近更新 更多