jOOQ - 分组依据使用别名而不是列

jOOQ - group by uses alias instead of columns

我是运行 jOOQ 3.13.6,在Oracle 11g springboot 环境上。

要使用 listagg 函数,我正在尝试此处提供的解决方案:

示例代码:

    @Autowired
    private DSLContext dsl;

    // setting tables
    Table table1 = DSL.table("table_1").as("t1");
    Table table2 = DSL.table("table_2").as("t2");

    // creating fields
    final List<Field<?>> fields = new ArrayList<Field<?>>();

    fields.add(DSL.field(DSL.name("t1", "t1_id")).as("id"));
    Field cf = DSL.field(DSL.name("t2", "t2_code")).as("code");
    fields.add(listAgg(cf, ";", null)); // no order

    // running query
    dsl.settings().withRenderQuotedNames(RenderQuotedNames.EXPLICIT_DEFAULT_UNQUOTED);

    dsl.select(fields)
            .from(table1)
            .join(table2).on("t1.t1_id = t2.t1_id")
            .where("t1.t1_id = ?", id)
            .groupBy(fields)

但是,它执行以下查询:

select t1.t1_id      id,
       listagg(t2.t2_code) within group (null) as code
  from table_1 t1
  join table_2 t2 on ( t1.t1_id = t2.t2_id )
 where t1.t1_id = 1
 group by id

group by 应使用原始列名称 (t1.t1_id),而不是别名。

请注意,为了这个答案,并且为了简洁起见,我假设您一直在使用 code generator。没有使用代码生成,答案是一样的。

为什么现在的行为?

Note, this behaviour is also documented here in the manual.

在 jOOQ 中,别名列表达式 T1.T1_ID.as("id") 只能生成其自身的 2 个不同版本:

  • T1.T1_ID as ID,即别名声明(在 SELECT 内部时,在顶层)
  • ID,即别名引用(当位于 SELECT 子句以外的任何其他子句/表达式内时)

没有生成的第三种类型 SQL 取决于您嵌入别名表达式的位置,例如当您将表达式放在 WHEREGROUP BY 等中时,无别名的列表达式 T1.T1_ID 。基本原理很简单。用户在编写时会期望什么:

groupBy(T1.T1_ID.as("id"))

为什么他们期望 as() 调用是 no-op?那会比现状更令人惊讶。

与其他渲染模式的一致性

jOOQ 中还有其他类型的 QueryPart,它们具有类似的别名功能:

  • Field
  • Table
  • WindowSpecification
  • Parameter
  • CTE

让我们看一下 CTE 示例:

Table<?> cte = name("cte").as(select(...))

cte 引用有 2 种渲染模式 SQL:

  • CTE 声明(如果放在 WITH 子句中)
  • CTE 参考(如果放在 FROM 等)

我认为您不会期望 cte 引用忽略别名,只渲染 SELECT 本身?

同样使用 table 别名:

T1 x = T1.as("x");

这可以呈现为:

  • 别名声明(如果放在 FROM 子句中)
  • 别名引用

因为 FROM 子句是 logically before 任何其他子句,你永远不会期望你的 x 引用只呈现 T1,而不是 xT1 as x,对吗?

因此,出于 jOOQ API 一致性的原因,Field 别名的行为也必须与所有其他别名一样。

该怎么做?

不要 re-use 别名表达式位于 SELECT 子句之外。写 jOOQ SQL 就像你写实际的 SQL:

ctx.select(T1.T1_ID.as("id"), ...)
   .from(T1)
   .groupBy(T1.T1_ID)
   ...