Jooq ERROR: missing FROM-clause entry for table for nested query(sum and group by)

Jooq ERROR: missing FROM-clause entry for table for nested query(sum and group by)

我在 Postgres 9.6 上执行了以下 jooq 语句:

DSLContext context = DSL.using(connection, POSTGRES_10);
testSafesFundsAllocationRecord record = 
context.select()
       .from(
          select(
            TEST_SAFES_FUNDS_ALLOCATION.LEDGER_ID,       
            sum(TEST_SAFES_FUNDS_ALLOCATION.AMOUNT)
               .as(TEST_SAFES_FUNDS_ALLOCATION.AMOUNT))
         .from(TEST_SAFES_FUNDS_ALLOCATION)
         .groupBy(TEST_SAFES_FUNDS_ALLOCATION.LEDGER_ID))
       .where(TEST_SAFES_FUNDS_ALLOCATION.AMOUNT.greaterOrEqual(amount))
       .and(TEST_SAFES_FUNDS_ALLOCATION.LEDGER_ID.notIn(excludedLedgers))
       .orderBy(TEST_SAFES_FUNDS_ALLOCATION.AMOUNT.asc())
       .limit(1)
       .fetchOne()
       .into(TEST_SAFES_FUNDS_ALLOCATION);

翻译如下:

SQL [
select "alias_88420990"."ledger_id", "alias_88420990"."amount" from (
select "public"."test_safes_funds_allocation"."ledger_id", 
sum("public"."test_safes_funds_allocation"."amount") as "amount" from "public"."test_safes_funds_allocation" group by "public"."test_safes_funds_allocation"."ledger_id") as "alias_88420990"
 where ("public"."test_safes_funds_allocation"."amount" >= ? and 1 = 1) order by "public"."test_safes_funds_allocation"."amount" asc limit ?]; 

excludedLedgers -> 是一个空字符串数组

结果是:

org.postgresql.util.PSQLException: ERROR: missing FROM-clause entry for table "test_safes_funds_allocation"
  Position: 334

谁能告诉我查询中的问题是什么?如您所见,它是嵌套的...但我似乎无法理解这个问题。 查询执行以下操作: 对同一分类帐 id(分组依据)的所有数量行求和,然后从输出 return 数量大于变量数量的最小行,我们可以通过数组排除特定的 ledger_id(它是在此示例中为空)。

任何帮助将不胜感激,

此致

未命名派生 table 仅在少数 SQL 方言(例如 Oracle)中受支持,而不是在所有方言中(例如在 PostgreSQL 中不支持)。这就是为什么 jOOQ 会为每个派生的 table 生成一个别名。现在您的派生 table 已使用别名,您无法再使用原始 table 名称访问其列,但您必须改用别名。您可以在生成的 SQL 查询中看到哪里出了问题:

select 
  "alias_88420990"."ledger_id", -- These are correctly referenced, because you used
  "alias_88420990"."amount"     -- select(), so jOOQ did the dereferencing for your
from (
  select 
    "public"."test_safes_funds_allocation"."ledger_id", 
    sum("public"."test_safes_funds_allocation"."amount") as "amount"
  from "public"."test_safes_funds_allocation" 
  group by "public"."test_safes_funds_allocation"."ledger_id"
) as "alias_88420990" -- This alias is generated by jOOQ

-- In these predicates, you're referencing the original column name with full qualification
-- when you should be referncing the column from alias_88420990 instead
where ("public"."test_safes_funds_allocation"."amount" >= ? and 1 = 1) 
order by "public"."test_safes_funds_allocation"."amount" asc 
limit ?

规范修复

所以您的 jOOQ 查询可以这样重写,以获得正确的 table 名称:

// Create a local variable to contain your subquery. Ideally, provide an explicit alias
Table<?> t = table(
    select(
        TEST_SAFES_FUNDS_ALLOCATION.LEDGER_ID,       
        sum(TEST_SAFES_FUNDS_ALLOCATION.AMOUNT)
           .as(TEST_SAFES_FUNDS_ALLOCATION.AMOUNT))
   .from(TEST_SAFES_FUNDS_ALLOCATION)
   .groupBy(TEST_SAFES_FUNDS_ALLOCATION.LEDGER_ID)).as("t");

// Now, use t everywhere, instead of TEST_SAFES_FUNDS_ALLOCATION
context.select()
       .from(t)
       .where(t.field(TEST_SAFES_FUNDS_ALLOCATION.AMOUNT).greaterOrEqual(amount))
       .and(t.field(TEST_SAFES_FUNDS_ALLOCATION.LEDGER_ID).notIn(excludedLedgers))
       .orderBy(t.field(TEST_SAFES_FUNDS_ALLOCATION.AMOUNT).asc())
       .limit(1)
       .fetchOne();

我正在使用 Table.field(Field) 从别名 table 中提取字段,而不会丢失类型信息。

改进修复,利用现有 table 类型

鉴于你的派生有两列,其名称也出现在原始 table 中,你可以使用 "trick" 从 jOOQ API 中获得更多类型安全,并且因此,这是一种取消引用列的更方便的方法。首先为 table 添加别名:

// This t reference now has all the column references like the original table
TestSafesFundsAllocation t = TEST_SAFES_FUNDS_ALLOCATION.as("t");

// The subquery is also named "t", but has a different definition
Table<?> subquery = table(
    select(
        TEST_SAFES_FUNDS_ALLOCATION.LEDGER_ID,       
        sum(TEST_SAFES_FUNDS_ALLOCATION.AMOUNT)
           .as(TEST_SAFES_FUNDS_ALLOCATION.AMOUNT))
   .from(TEST_SAFES_FUNDS_ALLOCATION)
   .groupBy(TEST_SAFES_FUNDS_ALLOCATION.LEDGER_ID)).as(t);

// Now, select again from the subquery, but dereference columns from the aliased table t
context.select()
       .from(subquery)
       .where(t.AMOUNT.greaterOrEqual(amount))
       .and(t.LEDGER_ID.notIn(excludedLedgers))
       .orderBy(t.AMOUNT.asc())
       .limit(1)
       .fetchOne();

记住,这是一个技巧,只有派生的 table 的列与派生的 table 的原始 table 具有相同的名称和类型时才有效选择自。

重写你的SQL

Derived tables(和常见的 table 表达式)是 jOOQ 的 DSL 不如原生 SQL 强大的领域,因为 jOOQ 无法轻松地对派生 table 以通常的方式。这就是必须使用局部变量和类型不安全取消引用的原因。

通常,如果这是一个选项,这个警告是完全避免派生 table 的充分理由。在您的情况下,您不需要派生 table。更好的查询(在本机 SQL 中甚至更好)是:

context.select(
          TEST_SAFES_FUNDS_ALLOCATION.LEDGER_ID, 
          sum(TEST_SAFES_FUNDS_ALLOCATION.AMOUNT).as(TEST_SAFES_FUNDS_ALLOCATION.AMOUNT))
       .from(TEST_SAFES_FUNDS_ALLOCATION)
       .where(TEST_SAFES_FUNDS_ALLOCATION.LEDGER_ID.notIn(excludedLedgers))
       .groupBy(TEST_SAFES_FUNDS_ALLOCATION.LEDGER_ID)
       .having(sum(TEST_SAFES_FUNDS_ALLOCATION.AMOUNT).greaterOrEqual(amount))
       .orderBy(sum(TEST_SAFES_FUNDS_ALLOCATION.AMOUNT).asc())
       .limit(1)
       .fetchOne()