0

So I have five tables I am trying to get back in one query

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)

I am trying to list all questions

Error is Unknown column 'ideas_service.parent_section.id' in 'on clause'

Here is my query

        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()

Previously when I was only trying to query the table for three tables, parent_Section_assessment_question and assessment_question_multi_choice, my query was like this and it worked

    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()

How should I modify the working query to also list a join between a assessment_Answer_Group( where assessment_answer_Group.entity_id = x) on the questions assessment_answer.answer_group_id

To clarify on the joining structure

I am doing an inner join between parent_section and assessment_Question and a left join between assessment_question and assessment_multi_choice_option

The join between answer_group and assessment_answer is separate from those three tables, it should be where assessment_answer_group.entity_id = x and join on assessment_Answer.answer_Group_id = assessment_answer_Grorup.id

edit: After akinas comments my query is now this, it finds the assessment_answer_group but it does not find any 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))
Daniel Haughton
  • 1,085
  • 5
  • 20
  • 45
  • It seems that `.from(PARENT_SECTION,ASSESSMENT_ANSWER_GROUP)` is not equal to `.from(PARENT_SECTION).join(ASSESSMENT_ANSWER_GROUP)` - the cartesian may obtain some unknown intermediate name, so `PARENT_SECTION` table name alias become invalid. If each `.join` needs in its own `.on` then try `.from(PARENT_SECTION).join(ASSESSMENT_ANSWER_GROUP).on(1)`. – Akina Jun 02 '20 at 08:43
  • So I am not trying to join parent_section and assessment_answer_group, I edited the OP to see if that clarify things – Daniel Haughton Jun 02 '20 at 08:54
  • *I am not trying to join parent_section and assessment_answer_group* In MySQL syntax (you have tagged the question as MySQL-relative) the comma between tablenames means low-priority CROSS JOIN of these tables. So in `t1, t2 join t3` joining of `t2` and `t3` is performed firstly, and this joining cannot access `t1` columns because `t1` is not joined yet. – Akina Jun 02 '20 at 08:58
  • Ah, i suppose I am using JOOQ incorrectly then. I did not intend to cross join those two tables, i thought Select( TABLE1, TABLE2) was just saying select from both those tables – Daniel Haughton Jun 02 '20 at 09:05
  • *i thought Select( TABLE1, TABLE2) was just saying select from both those tables* That's true. In MySQL `FROM t1,t2`, `FROM t1 CROSS JOIN t2`, `FROM t1 JOIN t2` and `FROM t1 INNER JOIN t2` (the last two - without ON clause) are aliases and causes selection from both tables (rather then in other DBMS where last two variants MUST have `ON` clause). The only difference in comma syntax is its low priority. – Akina Jun 02 '20 at 09:12
  • Thank you, I did .from(PARENT_SECTION.join(ASSESSMENT_ANSWER_GROUP).on(true)) and it works – Daniel Haughton Jun 02 '20 at 09:16
  • I think that outer parenthesis are excess - they cannot affect on final result but they fix table scanning order which may decrease performance. – Akina Jun 02 '20 at 09:18
  • @Akina actualy it does not work fully. It gets the assessment_answer_group but no assessment_answers, I have updated OP with current query – Daniel Haughton Jun 02 '20 at 09:24
  • After applying LEFT JOIN to some table (ASSESSMENT_QUESTION_MULTI_CHOICE_OPTION in your case) you MUST apply LEFT (not INNER !) JOIN to all another tables (ASSESSMENT_ANSWER in your case) which are joined to this table (and by-chain) - if not then your LEFT JOIN will be converted into INNER JOIN implicitly. – Akina Jun 02 '20 at 09:27
  • I changed everything to left join, it is still the same, No answer is returned – Daniel Haughton Jun 02 '20 at 09:34
  • My mistake. I actually had a small error in mapping it in java, it works. Thanks alot for the help – Daniel Haughton Jun 02 '20 at 09:37

1 Answers1

0

Why are you getting the syntax error

The reason why you're getting this error:

Error is Unknown column 'ideas_service.parent_section.id' in 'on clause'

Is because you're joining ASSESSMENT_QUESTION to ASSESSMENT_ANSWER_GROUP, not to PARENT_SECTION in your jOOQ query:

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

As others have pointed out in the comments, you're mixing comma separated from lists with ANSI JOIN syntax. This is hardly ever recommended, both in SQL, or in jOOQ. In jOOQ, your above query corresponds to this SQL:

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

As you can see, the join expression doesn't make any sense because you're joining two tables using a predicate that references a table that is not in scope. I recently wrote a blog post that might help understanding SQL JOIN syntax by adding such parentheses. I think the parentheses definitely help here.

Here's my advice on getting joins right:

  • Never mix table lists with ANSI joins, it's usually just trouble (although, it can be useful in edge cases). If you need outer joins, you cannot use table lists in most SQL dialects (except for Oracle), so you need ANSI joins anyway. Stick to them.
  • Be careful of mixing outer joins with inner joins. Joins are operators, and it's important to be aware of their associativity

Cartesian products

But you have another problem. You cannot easily join several to-many relationships in SQL, or you'll get cartesian products. Meaning:

  • A PARENT_SECTION can have many ASSESSMENT_QUESTION, which can have many ASSESSMENT_QUESTION_MULTI_CHOICE_OPTION. These are 2 levels of to-many relationships, which will duplicate the PARENT_SECTION per ASSESSMENT_QUESTION and again the ASSESSMENT_QUESTION per ASSESSMENT_QUESTION_MULTI_CHOICE_OPTION. This repeated duplication is OK, because you're going down one branch of your join tree.
  • However, an ASSESSMENT_QUESTION also has several ASSESSMENT_ANSWER, because that's another to-many relationship. This is causing problems.

So, you'll end up with a cartesian product between ASSESSMENT_QUESTION_MULTI_CHOICE_OPTION and ASSESSMENT_ANSWER, as you're traversing two separate to-many branches of your join tree, which is definitely not what you want.

While your 5-join query might eventually be right syntactically, it will still be wrong semantically!

Getting it right

You will have to resort to using either XML/JSON (available from jOOQ 3.14), or run (at least) 2 queries:

// 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();

You can then assemble the results together in your client. Notice the second query doesn't need to join PARENT_SECTION and ASSESSMENT_QUESTION anymore. A semi join (implemented using the IN predicate) will suffice

Using implicit joins

jOOQ supports implicit joins for to-one relationships. The above Query 2 could also be written like this, which is perhaps a bit simpler (although I do recommend you study "real" joins first):

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 Eder
  • 211,314
  • 129
  • 689
  • 1,509
  • Thank you for the very detailed response Lukas. But I am trying to understand with what is the problem with what my current approach is. You mention that "as you're traversing two separate to-many branches of your join tree, which is definitely not what you want" What is the problem with this beside having the handle the duplication in my client? – Daniel Haughton Jun 02 '20 at 10:06
  • Well, you get `O(N^2)` too much data, which will kill your performance, apart from in some cases of cartesian, de-duplication being impossible. – Lukas Eder Jun 02 '20 at 10:28
  • What instances could de-duplication be impossible? Could it happen in my case? – Daniel Haughton Jun 02 '20 at 10:55
  • @DanielHaughton: Imagine `{A, B} x {1, 2} = {(A, 1), (A, 2), (B, 1), (B, 2)}`. Now, imagine that `(B, 1)` is a combination that is never possible in your data set / domain, for whatever reason. But you have it in the result, because you created an undesired cartesian product. It would just be wrong. I don't know if it can happen in your data set, but why take chances? – Lukas Eder Jun 02 '20 at 15:41