Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jOOQ - join with nested subquery

Let's say I want to find out who wrote CLRS in a book db (tables BOOK, AUTHOR with a junction table BOOK_AUTHOR).

SelectConditionStep<Record1<String>> query = create
        .select(AUTHOR.LASTNAME.as("AuthorName"))
            .from(
                    (
                            BOOK.leftOuterJoin(BOOK_AUTHOR).on(BOOK.ID.eq(BOOK_AUTHOR.BOOKID))
                    ).leftOuterJoin(AUTHOR).on(AUTHOR.ID.eq(BOOK_AUTHOR.AUTHORID))
            )
            .where(BOOK.TITLE.eq(CLRS_title))
        ;

A bit inefficient to match the entire table, just to select a single book. I now want to select that book prior to the match.

The jOOQ doc on this matter led me to believe that could look something like this:

Table<Record1<Integer>> clrs = create
        .select(BOOK.ID.as("bookID"))
            .from(BOOK)
            .where(BOOK.TITLE.eq(CLRS_title))

        .asTable()
        ;

SelectJoinStep<Record1<String>> query = create
        .select(AUTHOR.LASTNAME.as("AuthorName"))
            .from(
                    (
                            clrs.leftOuterJoin(BOOK_AUTHOR).on(clrs.field("bookID").eq(BOOK_AUTHOR.BOOKID))
                    ).leftOuterJoin(AUTHOR).on(AUTHOR.ID.eq(BOOK_AUTHOR.AUTHORID))
            )
        ;

However, that fails to compile because

Cannot resolve method 'eq(org.jooq.TableField<ch.cypherk.bookdb.public_.tables.records.BookAuthorRecord,java.lang.Integer>)'

in the join condition.

What's the correct way to write this join?

like image 989
User1291 Avatar asked Mar 19 '19 10:03

User1291


People also ask

Can we replace join with subquery?

A join returns a result table constructed from data from multiple tables. You can also retrieve the same result table using a subquery. A subquery is simply a SELECT statement within another select statement.

Can we join tables using subquery?

yes, sql works on sets, a subquery returns a set as result, so this is possible.

Can we use inner join in subquery?

With INNER JOIN your Sub-Query will be execute only once and its records may gets stored internally in tempdb worktable on complex operations, then JOINed with the 1st table. With APPLY clause, the Sub-Query will be executed for every row in the 1st table.


1 Answers

The problem you're having

You're dereferencing a column from your derived table using Table.field(String):

clrs.field("bookID")

The type you're getting back is Field<?>, with a wildcard. Like with any generic type, once you have a wild card, a lot of operations (but not all) will no longer be possible on that type. Take List<?>, for example. You can still call List<?>.get() to retrieve an Object, but not List<?>.add(? element). In Field<?>, you can no longer call eq(), unless you cast the argument to a raw type.

You can also coerce your field's <T> type to the type you already know, e.g. by using Table.field(String, DataType<T>)

clrs.field("bookID", BOOK.ID.getDataType())

Study your various options and you might discover the one(s) you might find most useful

A better solution to your query

You don't really need to

  1. Assign your subquery to a local variable
  2. Use a derived table for your problem

Often with jOOQ, if you're having issues with derived tables as above, ask yourself is there really not an easier query I could write instead?

What you really need here is a semi join. Write:

// Assuming this static import
import static org.jooq.impl.DSL.*;

ctx.select(AUTHOR.LASTNAME)
   .from(AUTHOR)
   .where(AUTHOR.ID.in(
        select(BOOK_AUTHOR.AUTHORID)
       .from(BOOK_AUTHOR)
       .join(BOOK).on(BOOK.ID.eq(BOOK_AUTHOR.BOOKID))
       .where(BOOK.TITLE.eq(clrsTitle))
   )
   .fetch();
like image 130
Lukas Eder Avatar answered Sep 18 '22 17:09

Lukas Eder