【问题标题】:JOOQ, Unknown column 'ideas_service.parent_section.id' in 'on clause' when joining multiple tablesJOOQ,连接多个表时,“on 子句”中的未知列“ideas_service.parent_section.id”
【发布时间】:2020-09-20 16:16:26
【问题描述】:

所以我有五个表,我试图在一个查询中返回

parent_section(id, gate_id)


Assessment_Question(id,parent_section_id)


Assessment_question_multi_choice_option(id, question_id)


Assessment_answer_Group(id, entity_id)


Assessment_Answer(id,assessment_question_id, assessment_answer_Group_id)

我正在尝试列出所有问题

错误是Unknown column 'ideas_service.parent_section.id' in 'on clause'

这是我的查询

        dsl.select()
        .from(PARENT_SECTION,ASSESSMENT_ANSWER_GROUP)
        .join(ASSESSMENT_QUESTION)
        .on(PARENT_SECTION.ID.eq(ASSESSMENT_QUESTION.PARENT_SECTION_ID))
        .leftJoin(ASSESSMENT_QUESTION_MULTI_CHOICE_OPTION)
        .on(
            ASSESSMENT_QUESTION_MULTI_CHOICE_OPTION.ASSESSMENT_QUESTION_ID.eq(
                ASSESSMENT_QUESTION.ID))
        .join(ASSESSMENT_ANSWER)
        .on(ASSESSMENT_ANSWER_GROUP.ID.eq(ASSESSMENT_ANSWER.ASSESSMENT_ANSWER_GROUP_ID))
        .where(PARENT_SECTION.GATE_ID.eq(gateId))
        .fetch()

以前,当我只尝试查询表中的三个表时,parent_Section_assessment_question 和assessment_question_multi_choice,我的查询是这样的,并且有效

    var queryResult =
    dsl.select()
        .from(PARENT_SECTION)
        .join(ASSESSMENT_QUESTION)
        .on(PARENT_SECTION.ID.eq(ASSESSMENT_QUESTION.PARENT_SECTION_ID))
        .leftJoin(ASSESSMENT_QUESTION_MULTI_CHOICE_OPTION)
        .on(
            ASSESSMENT_QUESTION_MULTI_CHOICE_OPTION.ASSESSMENT_QUESTION_ID.eq(
                ASSESSMENT_QUESTION.ID))
        .where(PARENT_SECTION.GATE_ID.eq(gateId))
        .fetch()

我应该如何修改工作查询以同时列出问题assessment_answer.answer_group_id 上的assessment_Answer_Group(whereassessment_answer_Group.entity_id = x) 之间的联接

澄清连接结构

我在 parent_section 和assessment_Question 之间进行内部联接,在assessment_question 和assessment_multi_choice_option 之间进行左联接

answer_group 和assessment_answer 之间的连接与这三个表是分开的,它应该是assessment_answer_group.entity_id = x 并在assessment_Answer.answer_Group_id =assessment_answer_Grorup.id 上连接

编辑:在 akinas cmets 我的查询现在是这样的,它找到了assessment_answer_group,但没有找到任何assessment_answers

    var queryResult =
    dsl.select()
      //below line is cross join with an always true join coniditon
        .from(PARENT_SECTION.join(ASSESSMENT_ANSWER_GROUP).on(true))
        .join(ASSESSMENT_QUESTION)
        .on(PARENT_SECTION.ID.eq(ASSESSMENT_QUESTION.PARENT_SECTION_ID))
        .leftJoin(ASSESSMENT_QUESTION_MULTI_CHOICE_OPTION)
        .on(
            ASSESSMENT_QUESTION_MULTI_CHOICE_OPTION.ASSESSMENT_QUESTION_ID.eq(
                ASSESSMENT_QUESTION.ID))
        .join(ASSESSMENT_ANSWER)
        .on(ASSESSMENT_ANSWER_GROUP.ID.eq(ASSESSMENT_ANSWER.ASSESSMENT_ANSWER_GROUP_ID))
        .where(PARENT_SECTION.GATE_ID.eq(gateId))

【问题讨论】:

  • 看来.from(PARENT_SECTION,ASSESSMENT_ANSWER_GROUP)不等于.from(PARENT_SECTION).join(ASSESSMENT_ANSWER_GROUP)——笛卡尔可能会得到一些未知的中间名,所以PARENT_SECTION表名别名失效。如果每个.join 都需要自己的.on,那么试试.from(PARENT_SECTION).join(ASSESSMENT_ANSWER_GROUP).on(1)
  • 所以我不想加入 parent_section 和assessment_answer_group,我编辑了 OP 看看是否能澄清事情
  • 我不想加入 parent_section 和assessment_answer_group 在 MySQL 语法中(您已将问题标记为 MySQL-relative),表名之间的逗号表示这些的低优先级 CROSS JOIN表。所以在t1, t2 join t3中首先进行t2t3的连接,这个连接不能访问t1列,因为t1还没有连接。
  • 啊,我想我当时错误地使用了 JOOQ。我不打算交叉加入这两个表,我认为 Select(TABLE1, TABLE2) 只是说从这两个表中选择
  • 我认为 Select(TABLE1, TABLE2) 只是说从这两个表中选择这是真的。在 MySQL 中,FROM t1,t2FROM t1 CROSS JOIN t2FROM t1 JOIN t2FROM t1 INNER JOIN t2(最后两个 - 没有 ON 子句)是别名并导致从两个表中进行选择(而不是在其他 DBMS 中最后两个变体必须有 ON 子句)。逗号语法的唯一区别是它的优先级较低。

标签: mysql jooq


【解决方案1】:

为什么会出现语法错误

您收到此错误的原因:

错误是“on 子句”中的未知列“ideas_service.parent_section.id”

是因为您在 jOOQ 查询中加入了 ASSESSMENT_QUESTIONASSESSMENT_ANSWER_GROUP,而不是 PARENT_SECTION

dsl.select()
   .from(PARENT_SECTION,ASSESSMENT_ANSWER_GROUP)
   .join(ASSESSMENT_QUESTION)
   .on(PARENT_SECTION.ID.eq(ASSESSMENT_QUESTION.PARENT_SECTION_ID))
   // ...

正如其他人在 cmets 中指出的那样,您正在使用 ANSI JOIN 语法将逗号与列表分隔开来。这在 SQL 或 jOOQ 中几乎不被推荐。在jOOQ中,你上面的查询对应这个SQL:

SELECT *
FROM PARENT_SECTION, (
  ASSESSMENT_ANSWER_GROUP 
    JOIN ASSESSMENT_QUESTION 
      ON PARENT_SECTION.ID = ASSESSMENT_QUESTION.PARENT_ID
  )
..

如您所见,连接表达式没有任何意义,因为您使用的谓词连接两个表,该谓词引用了一个不在范围内的表。 I recently wrote a blog post that might help understanding SQL JOIN syntax by adding such parentheses。我认为括号在这里肯定有帮助。

这是我对正确连接的建议:

  • 切勿将表列表与 ANSI 连接混合使用,这通常很麻烦(尽管它在边缘情况下很有用)。如果您需要外连接,则在大多数 SQL 方言(Oracle 除外)中都不能使用表列表,因此无论如何您都需要 ANSI 连接。坚持下去。
  • 小心混合外部连接和内部连接。连接是运算符,it's important to be aware of their associativity

笛卡尔积

但是你还有另一个问题。您不能在 SQL 中轻松加入多个对多关系,否则您将获得笛卡尔积。含义:

  • 一个PARENT_SECTION可以有很多ASSESSMENT_QUESTION,也可以有很多ASSESSMENT_QUESTION_MULTI_CHOICE_OPTION。这是 2 个级别的多对多关系,它们将复制每个 ASSESSMENT_QUESTIONPARENT_SECTION 和每个 ASSESSMENT_QUESTION_MULTI_CHOICE_OPTIONASSESSMENT_QUESTION。这种重复的复制是可以的,因为您将沿着连接树的一个分支向下移动。
  • 然而,一个ASSESSMENT_QUESTION 也有几个ASSESSMENT_ANSWER,因为那是另一种对多的关系。这会导致问题。

因此,您最终会得到ASSESSMENT_QUESTION_MULTI_CHOICE_OPTIONASSESSMENT_ANSWER 之间的笛卡尔积,因为您正在遍历连接树的两个独立的多对多分支,这绝对不是您想要的。

虽然您的 5 连接查询最终可能在句法上是正确的,但在语义上仍然是错误的!

做对了

您必须resort to using either XML/JSON (available from jOOQ 3.14),或运行(至少)2 个查询:

// Query 1 producing ASSESSMENT_QUESTION_MULTI_CHOICE_OPTION
dsl.select()
   .from(PARENT_SECTION)
   .join(ASSESSMENT_QUESTION)
     .on(ASSESSMENT_QUESTION.PARENT_SECTION_ID.eq(PARENT_SECTION.ID))
   .leftJoin(ASSESSMENT_QUESTION_MULTI_CHOICE_OPTION)
     .on(ASSESSMENT_QUESTION_MULTI_CHOICE_OPTION.ASSESSMENT_QUESTION_ID
       .eq(ASSESSMENT_QUESTION.ID))
   .where(PARENT_SECTION.GATE_ID.eq(gateId))
   .fetch();

// Query 2 producing ASSESSMENT_ANSWER and ASSESSMENT_ANSWER_GROUP
dsl.select()
   .from(ASSESSMENT_ANSWER)
   .join(ASSESSMENT_ANSWER_GROUP)
     .on(ASSESSMENT_ANSWER_GROUP.ID.eq(ASSESSMENT_ANSWER.ASSESSMENT_ANSWER_GROUP_ID))
   .where(ASSESSMENT_ANSWER.ASSESSMENT_QUESTION_ID.in(
     select(ASSESSMENT_QUESTION.ID)
     .from(ASSESSMENT_QUESTION)
     .join(PARENT_SECTION)
     .on(ASSESSMENT_QUESTION.PARENT_SECTION_ID.eq(PARENT_SECTION.ID))
     .where(PARENT_SECTION.GATE_ID.eq(gateId))
   ))
   .fetch();

然后,您可以在客户端中将结果组合在一起。请注意,第二个查询不再需要加入 PARENT_SECTIONASSESSMENT_QUESTIONsemi join(使用IN predicate 实现)就足够了

使用隐式连接

jOOQ supports implicit joins for to-one relationships。上面的 Query 2 也可以这样写,可能会更简单一些(虽然我确实建议你先研究“真正的”连接):

dsl.select()
   .from(ASSESSMENT_ANSWER)
   .join(ASSESSMENT_ANSWER_GROUP)
     .on(ASSESSMENT_ANSWER_GROUP.ID.eq(ASSESSMENT_ANSWER.ASSESSMENT_ANSWER_GROUP_ID))
   .where(ASSESSMENT_ANSWER.assessmentQuestion().parentSection().GATE_ID.eq(gateId))
   .fetch();

【讨论】:

  • 感谢 Lukas 非常详细的回复。但我试图了解我目前的方法有什么问题。您提到“当您遍历连接树的两个独立的多对多分支时,这绝对不是您想要的”除了处理我的客户端中的重复之外,还有什么问题?
  • 好吧,你得到O(N^2) 太多的数据,这会影响你的表现,除了在某些笛卡尔的情况下,重复数据删除是不可能的。
  • 哪些情况下重复数据删除是不可能的?会不会发生在我身上?
  • @DanielHaughton:想象一下{A, B} x {1, 2} = {(A, 1), (A, 2), (B, 1), (B, 2)}。现在,想象一下(B, 1) 是一个在您的数据集/域中永远不可能的组合,无论出于何种原因。但是你在结果中有它,因为你创建了一个不想要的笛卡尔积。那就错了。我不知道它是否会发生在您的数据集中,但为什么要冒险呢?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多