如何在 jOOQ 中表示自连接?

How does one represent a self-join in jOOQ?

我有以下架构 (SQLite) 用于捕获来自 Messenger 的消息:

create table if not exists spot_message
(
    id             unsigned big int primary key not null,
    messenger_id   text not null,
    messenger_name text not null,
    message_type   text not null,
    timestamp      text not null,
    latitude       real not null,
    longitude      real not null
);

我使用以下自连接来查找每个发件人的最新消息:

select t1.*
from spot_message t1
         join (select messenger_id, max(timestamp) timestamp
               from spot_message
               group by messenger_id) t2 on t1.messenger_id = t2.messenger_id and t1.timestamp = t2.timestamp;

我不清楚如何在 jOOQ 中表示它。

我目前有:

DSL.using(c.get())
    .select(asterisk())
    .from(table("spot_message").as("t1"))
    .join(select(field("messenger_id"), max(field("timestamp"))).from(table("spot_message"))
        .groupBy(field("messenger_id")))
        .on(field("messenger_id")
                .eq(field("messenger_id"))
                .and(field("timestamp")
                        .eq(field("timestamp"))));

但不清楚我如何表达加入table(“t2”)的table名称的“as”。

回答您的问题

要将 Select 作为派生 table 的别名,您可以使用:

您正在使用 plain SQL API 构建查询,因此您可以在字符串中使用任何 SQL 表达式,例如"t2.messenger_id"。这应该有效:

DSL.using(c.get())
    .select(asterisk())
    .from(table("spot_message").as("t1"))
    .join(select(field("messenger_id"), max(field("timestamp")))
        .from(table("spot_message"))
        .groupBy(field("messenger_id")).asTable("t2")) // t2 alias here
    .on(field("t1.messenger_id") // Qualifications here, and below
        .eq(field("t2.messenger_id"))
    .and(field("t1.timestamp")
        .eq(field("t2.timestamp"))));

但是,如果您是 using code generation, which I recommend for various reasons

,这会更具可读性
SpotMessage t1 = SPOT_MESSAGE.as("t1");
SpotMessage t2 = SPOT_MESSAGE.as("t2");

DSL.using(c.get())
   .select(t1.asterisk())
   .from(t1)
   .join(
       select(t2.MESSENGER_ID, max(t2.TIMESTAMP).as(t2.TIMESTAMP))
       .from(t2)
       .groupBy(t2.MESSENGER_ID).asTable(t2))
   .on(t1.MESSENGER_ID.eq(t2.MESSENGER_ID))
   .and(t1.TIMESTAMP.eq(t2.TIMESTAMP));

使用 window 函数的替代方法

由于您使用的是 SQLite,它支持 window functions, why not just use those? You can even use the QUALIFY syntax,如果您使用的是商业发行版,jOOQ 可以为您模拟:

在SQL中:

select *
from spot_message
qualify timestamp = max(timestamp) over (partition by messenger_id)

在 jOOQ 中:

ctx.selectFrom(SPOT_MESSAGE)
   .qualify(SPOT_MESSAGE.TIMESTAMP.eq(
       max(SPOT_MESSAGE.TIMESTAMP).over(partitionBy(SPOT_MESSAGE.MESSENGER_ID))
   ));

如果 QUALIFY 对您不可用,您仍然可以通过将查询包装在派生的 table:

中手动模拟它
SELECT *
FROM (
  SELECT 
    spot_message.*, 
    timestamp = max(timestamp) over (partition by messenger_id) AS is_max
  FROM spot_message
) AS t
WHERE is_max