Does jOOQ support array of select query? I want something like the following:
select table.*, array(select another_table.id from another_table where ...)
from table;
I tried experimenting with DSL.array(context.select(...).asField()) but this generates array[(select ...)] instead of array(select(...)).
I should have done:
PostgresDSL.array(context.select(...))
Note that we are using PostgresDSL instead of the generic DSL and not applying .asField() to the select, to inline the inner select query into the outer query.
Related
I have a problem with jOOQ 3.8.
So, I have a table in PostgreSQL 9.5, something like.
CREATE TABLE my_table(
id bigserial,
types my_type[]
)
where the my_type is a type like
CREATE TYPE my_type AS(
id smallint,
something text
)
Now, in jOOQ 3.8 I want to do something like
dsl.selectDistinct(MY_TABLE.ID)
.from(MY_TABLE)
.join(TYPE_TABLE).on(TYPE_TABLE.ID.equal(DSL.any(
MY_TABLE.TYPES.ID
))
.fetch(MY_TABLE.ID);
Clearly the step in which I do MY_TABLE.TYPES.ID is wrong. I was thinking about using DSL.select(MY_TYPE.ID)... but clearly the my_type is a type, not a table.
How can I access type properties using jOOQ?
How to solve this with PostgreSQL
I don't think there's an easy way to transform your my_type[] into an integer[] type in PostgreSQL, extracting my_type.id for each value, for it to be usable with the any() operator.
But you can work around this limitation by using UNNEST(), something like this:
SELECT DISTINCT my_table.id
FROM my_table
CROSS JOIN LATERAL unnest(my_table.types)
The above will yield something like
id types id something
----------------------------------------------
1 {"(1,a)","(2,b)"} 1 a
1 {"(1,a)","(2,b)"} 2 b
2 {"(1,a)","(2,b)","(3,c)"} 1 a
2 {"(1,a)","(2,b)"} 2 b
2 {"(1,a)","(2,b)"} 3 c
Now this, you can join again to TYPE_TABLE, such as:
SELECT DISTINCT my_table.id
FROM my_table
CROSS JOIN LATERAL unnest(my_table.types) types
INNER JOIN type_table ON type_table.id = types.id
Or, probably better performing:
SELECT my_table.id
FROM my_table
WHERE EXISTS (
SELECT 1
FROM type_table
JOIN unnest(my_table.types) AS types ON type_table.id = types.id
)
How to solve this with jOOQ
jOOQ's unnest support is currently (as of version 3.8) rather simple, i.e. you don't get all the type information in the resulting table, which is why you need to do some plain SQL mingling. But it's certainly doable! Here's how:
create().select(MY_TABLE.ID)
.from(MY_TABLE)
.whereExists(
selectOne()
.from(unnest(MY_TABLE.TYPES).as("types",
MY_TYPE.ID.getName(),
MY_TYPE.SOMETHING.getName()
))
.join(TYPE_TABLE)
.on(TYPE_TABLE.ID.eq(field(name("types", MY_TYPE.ID.getName()),
MY_TYPE.ID.getDataType())))
)
.fetch(MY_TABLE.ID);
I'm using org.jooq.impl.Executor to create a query that I want to pass to JdbcOperations. I'm passing parameter values by using .where(Condition...).
After chaining methods for the executor, I get a Query object. The problem is when I call query.getSQL(), the returned query string contains parameters ?,?,? instead of my inserted values.
This is the code I'm using to build the SQL query.
Note that TableA has three foreign keys to TableB. I tried to use JOIN ON with OR to join TableA and TableB but the performance was too slow.
Query query = executor.select(fieldsToSelect)
.from("TableA")
.join("TableB").on("TableA.FirstForeignKey = TableB.TableBID")
.join("TableC")
.on("TableC.TableCID = TableB.TableCForeignKey")
.where(condition)
.union(executor.select(fieldsToSelect)
.from("TableA")
.join("TableB").on("TableA.SecondForeignKey = TableB.TableBID")
.join("TableC")
.on("TableC.TableCID = TableB.TableCForeignKey")
.where(condition)
.union(executor.select(fieldsToSelect)
.from("TableA")
.join("TableB").on("TableA.ThirdForeignKey = TableB.TableBID")
.join("TableC")
.on("TableC.TableCID = TableB.TableCForeignKey")
.where(condition)));
This is how I create Condition object for the executor:
MySQLFactory.fieldByName(Integer.class, TABLE_NAME_TABLEA, "TableAID")
.in(ArrayUtils.toObject(ids));
This is how I perform the query:
jdbcOperations.query(query.getSQL(),query.getBindValues().toArray(), myMapper);
How can I correctly map parameters with values and use the query with JdbcOperations?
By default, the Query.getSQL() method returns a query with bind variables (?). You'll then pass that SQL string to a PreparedStatement and bind the variables individually, extracting them first via Query.getBindValue()
An alternative version of the Query.getSQL(boolean) or Query.getSQL(ParamType) method will indicate to jOOQ that the bind variables should be inlined into the SQL string, in order to form a static SQL statement, not a prepared statement.
You can also tell jOOQ to generate all SQL statements as static statements by using StatementType.STATIC_STATEMENT on the Settings that you provide the jOOQ Configuration with.
This is all documented here:
http://www.jooq.org/doc/latest/manual/sql-building/bind-values/inlined-parameters/
I have an example MyTable with 3 columns - id, common_id, creation_date, where common_id groups entries.
Now I would like to select using CriteriaBuilder all newest entries from each group (that is for each common_id get me latest creation_date).
In SQL the query would look like this:
select * from MyTable where (common_id, creation_date) in (select common_id, max(creation_date) from MyTable group by common_id)
Now I have tried to create the where predicate by writing something like (cb is CriteriaBuilder, root is a Root):
cb.array(root.get('common_id'), cb.max(root.get('creation_date')))
.in(
query.subquery(MyTable.class)
.select(cb.array(root.get('common_id'), cb.max(root.get('creation_date'))))
.groupBy(root.get('common_id')))
But unfortunately cb.array is not an Expression (it's a CompoundSelect), so I cannot use .in() on it.
Thanks for pointers!
could you create it using JPQL? As far that I know, that is not possible.
I looked at the Spect (4.6.16 Subqueries) and it talk about "simples select expression":
simple_select_clause ::= SELECT [DISTINCT] simple_select_expression
I believe that only one return is possible, if you look at the examples there you will not find anything like it.
You will need to use NativeQuery for it.
I have this query:
Field<String> yearMonth = DSL.field("FORMATDATETIME({0}, 'yyyy-MM')",
String.class, LICENZE.CREATION_DATE).as("anno_mese");
List<Record3<Integer, String, String>> records =
create.select(DSL.count().as("num_licenze"), LICENZE.EDIZIONE, yearMonth).
from(LICENZE).
groupBy(LICENZE.EDIZIONE, yearMonth).
orderBy(yearMonth).
fetch();
this query generates:
select
count(*) "num_licenze",
"PUBLIC"."LICENZE"."EDIZIONE",
FORMATDATETIME("PUBLIC"."LICENZE"."CREATION_DATE", 'yyyy-MM') "anno_mese"
from "PUBLIC"."LICENZE"
group by
"PUBLIC"."LICENZE"."EDIZIONE",
"anno_mese"
order by "anno_mese" asc
executing it i get: Column "anno_mese" not found; SQL statement
Testing the generated query and removing the quotes from anno_mese in every parts of the query make the query works instead.
Is my query wrong or am I using jooq in the wrong way?
The alias in this query is not so important, I can run the query without using it too but just to understand how it works.
I am using h2 as database.
Thanks for the help
I suspect this is a bug in H2, which I've reported here, because the query looks fine to me. Here are some workarounds that you can do from the jOOQ side:
Don't reference the "anno_mese" column by name
While SQL is a bit repetitive otherwise, you won't notice the difference with jOOQ. I simply moved the as("anno_mese") method call into the SELECT clause. You don't really need it in the GROUP BY and ORDER BY clauses.
Field<String> yearMonth = DSL.field("FORMATDATETIME({0}, 'yyyy-MM')",
String.class, LICENZE.CREATION_DATE);
List<Record3<Integer, String, String>> records =
create.select(DSL.count().as("num_licenze"),
LICENZE.EDIZIONE,
yearMonth.as("anno_mese")).
from(LICENZE).
groupBy(LICENZE.EDIZIONE, yearMonth).
orderBy(yearMonth).
fetch();
Disable quoting in jOOQ generated queries
You can use jOOQ's Settings to prevent schema / table / column names from being quoted. Example:
DSLContext create = DSL.using(connection, SQLDialect.H2,
new Settings().withRenderNameStyle(RenderNameStyle.AS_IS);
Use upper case column names
This will probably work: DSL.field(...).as("ANNO_MESE")
I have following Oracle SQL query:
SELECT SUBSTR(col, 0, INSTR(col, REGEXP_SUBSTR(col, '\.\d+$')) -1) AS col_new, col as col_orig AS col_orig FROM tab;
I have data in table like:
col
ABC.A.01
ABC.A.02
Above query returns results like:
col_new col_orig
ABC.A ABC.A.01
ABC.A ABC.A.02
I am trying to migrate it to JPA named query. Till now I could make query only like this:
SELECT SUBSTRING(f.col, 0, LENGTH(f.col) - LOCATE('.', REVERSE(f.col))), f.col FROM tab f;
I did this as I was not able to find equivalent in JPA for Oracle's REGEXP_SUBSTR. My JPA named query fails in data examples like ABC.A.P01.
Can you please let me know how can I migrate my SQL query to JPA named query using equivalent for REGEXP_SUBSTR.
I found that there is no equivalent for REGEXP_SUBSTR in JPA. So I decided to stick to native query execution.
If you are using eclipselink, use SQL to integrate SQL within a JPQL statement. This provides an alternative to using native SQL queries simply because the query may require a function not supported in JPQL.
The SQL function includes both the SQL string (to inline into the JPQL statement) and the arguments to translate into the SQL string. Use a question mark character ( ? ) to define parameters within the SQL that are translated from the SQL function arguments.
You can use SQL to call database functions with non standard syntax, embed SQL literals, and perform any other SQL operations within JPQL. With SQL, you can still use JPQL for the query.
Example
select o from Entity o order by SQL('REGEXP_SUBSTR(?, ''[0-9]+'', 1, 1)', o.code)