JOOQ multiset throwing syntax exception - mysql

Recently moved to JOOQ 3.15.5 and tried the Multiset feature but it is throwing SQLSyntaxErrorException. Below is the query I wrote:
dslContext.select(
tableA.asterisk(),
multiset(
select(tableB.DELETED, tableB.VALUE)
.from(tableB)
.where(tableB.ORDER_ID.eq(tableA.ORDER_ID))
).as("bookingAdditions")
).from(tableA)
.where(tableA.BATCH_ID.greaterThan(batchId))
.fetchInto(BookingDto.class);
Here is the relations:
|tableA| 1 n |tableB|
| | --------------> | |
| | | |
-------- --------
(tableA) (tableB)
Here is the query that is being generated by JOOQ:
set #t = ##group_concat_max_len; set ##group_concat_max_len = 4294967295; select `tablea`.*, (select coalesce(json_merge_preserve('[]', concat('[', group_concat(json_array(`v0`, `v1`) separator ','), ']')), json_array()) from (select `tableb`.`deleted` as `v0`, `tableb`.`value` as `v1` from `db_name`.`booking_additions` as `tableb` where `tableb`.`order_id` = `tablea`.`order_id`) as `t`) as `bookingadditions` from `db_name`.`booking` as `tablea` where `tablea`.`batch_id` > 0; set ##group_concat_max_len = #t;
Here are exceptions:
org.jooq.exception.DataAccessException: SQL [set #t = ##group_concat_max_len; set ##group_concat_max_len = 4294967295; select `tablea`.*, (select coalesce(json_merge_preserve('[]', concat('[', group_concat(json_array(`v0`, `v1`) separator ','), ']')), json_array()) from (select `tableb`.`deleted` as `v0`, `tableb`.`value` as `v1` from `db_name`.`booking_additions` as `tableb` where `tableb`.`order_id` = `tablea`.`order_id`) as `t`) as `bookingadditions` from `db_name`.`booking` as `tablea` where `tablea`.`batch_id` > ?; set ##group_concat_max_len = #t;]; You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'set ##group_concat_max_len = 4294967295; select `tablea`.*, (select coalesce(jso' at line 1
at org.jooq_3.15.5.MYSQL.debug(Unknown Source)
at org.jooq.impl.Tools.translate(Tools.java:2988)
at org.jooq.impl.DefaultExecuteContext.sqlException(DefaultExecuteContext.java:639)
at org.jooq.impl.AbstractQuery.execute(AbstractQuery.java:349)
at org.jooq.impl.AbstractResultQuery.fetchLazy(AbstractResultQuery.java:295)
at org.jooq.impl.AbstractResultQuery.fetchLazyNonAutoClosing(AbstractResultQuery.java:316)
at org.jooq.impl.SelectImpl.fetchLazyNonAutoClosing(SelectImpl.java:2866)
at org.jooq.impl.ResultQueryTrait.collect(ResultQueryTrait.java:357)
at org.jooq.impl.ResultQueryTrait.fetchInto(ResultQueryTrait.java:1423)
at com.company.BookingDAO.fetchBookings(BookingDAO.java:118)
at
I am using Mysql: 5.7. What am I doing wrong? Any hint?

jOOQ generates three SQL statements separated by semicolons when you use GROUP_CONCAT() in a query. Unfortunately, the default behavior of MySQL is to disallow multiple queries in a single request.
You have to change your JDBC connection options to include allowMultiQueries.
Read more about it here: https://blog.jooq.org/mysqls-allowmultiqueries-flag-with-jdbc-and-jooq/

Regarding the multiple statements
In addition to what Bill Karwin said, you could also specify the Settings.renderGroupConcatMaxLenSessionVariable to be false and increase your server's ##group_concat_max_len variable for your session or your server yourself.
jOOQ's default here is to assume it's easier to add the JDBC connection flag rather than change the server settings, but both approaches are viable.
Regarding correlated derived tables
This particular emulation requires support for correlated derived tables, which MySQL 5.7 doesn't support, see https://github.com/jOOQ/jOOQ/issues/12045. You're not going to get a correlated MULTISET expression to work on MySQL 5.7, but you could write an almost equivalent MULTISET_AGG expression, like this:
dslContext.select(
tableA.asterisk(),
multisetAgg(tableB.DELETED, tableB.VALUE).as("bookingAdditions")
).from(tableA)
.join(tableB).on(tableB.ORDER_ID.eq(tableA.ORDER_ID))
.where(tableA.BATCH_ID.greaterThan(batchId))
// You can group by the primary key, or tableA.fields() if you don't have a PK
.groupBy(tableA.ID)
.fetchInto(BookingDto.class);
Unlike MULTISET, MULTISET_AGG produces NULL values instead of empty lists in case you're left joining tableB, as is usual for SQL aggregate functions. You could then use coalesce(multisetAgg(...), multiset(...)) as a workaround.

Related

Use parameters in SQL Server referencing MySQL datasource

I have to run a query where some data comes from a SQL Server Database and some from a MySQL database. I managed to achieve it, but the problem comes when using a parameter with the MySQL database.
Here the faulty query :
select sp.SLC_ID, a.ASS_NAM, p.PCE_NAM, p.PCE_DES, pr.PRF_NAM,
(
SELECT top 1 t2.CleTri
FROM (
SELECT CAST(t1.CLETRI as varchar) CleTri, CAST(t1.LIBELLE as varchar) Libelle
FROM OPENQUERY("PLMStock",'SELECT CLETRI, LIBELLE FROM produits') t1
) t2
WHERE t2.Libelle LIKE pr.PRF_NAM + '%'
) as CLETRI
from SELECT_PART sp
left join PART p on p.PCE_ID = sp.PCE_ID
left join ASSEMBLY a on a.ASS_ID = sp.ASS_ID
left join PROFILE pr on pr.PRF_ID = p.PRF_ID
where sp.SLC_ID = 2930 and p.PRD_ID is null
When running this query, I got that error:
Msg 468, Level 16, State 9, Line 9
Cannot resolve the collation conflict between "French_CI_AS" and
"Latin1_General_CI_AS" in like operation.
But when I hardcode the like parameter it works fine.
Can someone help me with it please?
LIKE comparisons are affected by collation.
https://learn.microsoft.com/en-us/sql/t-sql/statements/collations?view=sql-server-ver15
WHERE t2.Libelle COLLATE database_default LIKE pr.PRF_NAM + '%'
The error indicates differing collations between the two columns, you need to convert one or the other to match.
Does the following work for you?
WHERE t2.Libelle LIKE pr.PRF_NAM + '%' COLLATE French_CI_AS

SQLite / MySQL compatibility issue

I know SQL in SQLite is not completely implemented the same way as in MySql. My problem with the following queries is, that they are not compatible and I like to avoid a conditional if <DBMS> ... else
SQLite query
UPDATE sorties SET state = '#'
WHERE `key` IN (
SELECT `key` FROM sorties
INNER JOIN reports AS r
ON r.sortieId=sorties.`key`);
Error on MySQL:
SQL Error (1093): Table 'sorties' is specified twice, both as a target for 'UPDATE' and as a separate source for data
MySQL query (adapted from here)
UPDATE sorties AS s SET s.state='#'
WHERE s.`key` IN (
SELECT t.sortieId FROM (
SELECT r.sortieId AS sortieId
FROM reports AS r
INNER JOIN sorties AS sort
ON sort.`key`=r.sortieId)
AS t);
Error on SQLite:
SQLiteManager: Likely SQL syntax error: UPDATE sorties AS s SET s.state='#'
WHERE s.key IN ( SELECT t.sortieId FROM (
SELECT DISTINCT r.sortieId AS sortieId
FROM reports AS r
INNER JOIN sorties AS sort
ON sort.key=r.sortieId) AS t); [ near "AS": syntax error ]
Exception Name: NS_ERROR_FAILURE
Exception Message: Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [mozIStorageConnection.createStatement]
I can't figure out how to make this queries work on both systems equally!
All I want to have is, that each state of sorties must be '#' when it's key can be found in reports.sortieId.
Maybe there is a different approach for this?
Thank you
The first command reads the key value from the sorties table in the subquery, and then checks whether those key values exist in the sorties table in the outer statement. That check is superfluous; you can just compare the values to the ones in reports directly:
UPDATE sorties
SET state = '#'
WHERE key IN (SELECT sortieId
FROM reports);
As for the second command, SQLite does not support aliasing a table used in INSERT/UPDATE/DELETE because those commands work only on a single table. You can just remove the AS s and replace s with sorties everywhere.

SSIS Mysql dataflow multiple statement source

I am running into problems with SSIS(2012), mysql(odbc) and dataflow when my sql command is more than one statement. In addition I cannot start the statement with a comment either. This is not a problem when using oledb & sql server.
SELECT id FROM id_table WHERE type = 'product' INTO #id;
SELECT name FROM products WHERE id = #id;
This isn't the most relevant example. It's just to show what I'm doing. But this will not work with Mysql(ODBC) as my source. I get the following:
Table has no columns
My work around is using a sub query:
SELECT name FROM products WHERE id = (SELECT id from id_table WHERE type = 'product');
Also the below with a comment comes up with the same error:
/* Test comment */
SELECT name FROM products WHERE id = (SELECT id from id_table WHERE type = 'product');
Again, sql server(OLEDB), haven't tried ODBC, has no problems with either case.
My first thought is to check the driver and enable the "Multiple statements" flag, which is already checked. Is there a solution to this in the settings of the driver or in SSIS?
EDIT:
Additional examples:
This works with sql server and odbc source for a dataflow:
DECLARE #offer AS VARCHAR(8)
SET #offer = 'WDWD' + CONVERT(VARCHAR,1000 + DATEPART(wk,GETDATE()))
SELECT * FROM source WHERE cd = #offer
The rough equivalent with mysql and odbc source for a dataflow does not:
SET #offer = CONCAT('WHW410', LPAD(WEEKOFYEAR(NOW()), 2, '0'));
SELECT * FROM source WHERE cd = #offer;
I verified both of these are valid sql in SSMS and sqlyog.

Complex MySQL cmd with JOINS and Counters doesnt work

I am working on this SQLFiddle and cant get the command working. Here the command:
SET #n := 1;
SET #start := '2013-07-22 10:00:01';
SET #end := '2013-07-22 10:00:02';
SET #register := 40001;
SELECT * FROM
(
SELECT
`realvalues`.`Timestamp`,
`realvalues`.`Value` * `register`.`Factor`,
#x := #x + 1 AS rank
FROM
`realvalues`,
(SELECT #x := 0) t
WHERE
`realvalues`.`Register` = register AND
`realvalues`.`Timestamp` BETWEEN start AND end
JOIN
`register`
ON
`register`.`DeviceID` = `realvalues`.`DeviceID` AND
`register`.`Register` = `realvalues`.`Register`
) a
WHERE
rank MOD ? = n
Does anybody know where the command fails? MySQL error reporting isnt very usefull.
[EDIT] The Value is now duplicated with Factor.
There are many many things wrong with your query. However, the error that is being reported in your fiddle is:
...check the manual that corresponds to your MySQL server version for the right syntax to use near 'BETWEEN start AND end JOIN register ON ...
Your syntax for BETWEEN is incorrect. There should be no IS token before BETWEEN. Correct syntax is:
<value> BETWEEN <lower-bound-inclusive> AND <upper-bound-inclusive>
Other problems include:
start, end, and n are not columns
register (in the WHERE clause) is ambiguous
You have a JOIN clause after a WHERE clause
You do not specify an alias for the second column of your derived table a (perhaps not necessary but may cause issues)
Use of a ? parameter without a way to specify a value (although this is a limitation of SQL Fiddle and not necessarily a problem with your SQL statement)
i dont see usage of #start and #end
EDIT: now it works !
sqlfiddle.com/#!2/6dc97/50

MySQL: union query fails on MySQL 5.5, works on MySQL 5.1

I'm having trouble with a stubborn MySQL query. It's a quite long and complex query, sorry about that. I really did my best to find it myself but I could use a hand here.
The following query is used to emulate a full outer join of column information in the information_schema, and column information in a table called nexedit_schema. The PHP variable $db contains the target database that contains the latter table.
The query actually successfully runs on my own server (MySQL version 5.1), but fails to run on a friend's server (MySQL version 5.5). It claims that I have a syntax error near "ON (schema_tables.db_table..."
I'm probably overlooking something really stupid. Anyone kind enough to help me out?
SELECT information_schema.tables.table_name, schema_tables.db_table, schema_tables.ne_page
FROM information_schema.tables
LEFT JOIN (
(SELECT DISTINCT db_table, ne_page FROM {$db}.nexedit_schema)
AS schema_tables)
ON (schema_tables.db_table = information_schema.tables.table_name
COLLATE utf8_unicode_ci)
WHERE information_schema.tables.table_schema = '{$db}'
AND information_schema.tables.table_name NOT LIKE 'nexedit_%'
UNION
SELECT information_schema.tables.table_name, schema_tables.db_table, schema_tables.ne_page
FROM information_schema.tables
RIGHT JOIN (
(SELECT DISTINCT db_table, ne_page FROM {$db}.nexedit_schema)
AS schema_tables)
ON (schema_tables.db_table = information_schema.tables.table_name
COLLATE utf8_unicode_ci)
WHERE information_schema.tables.table_schema IS NULL
AND schema_tables.db_table NOT LIKE 'nexedit_%'
GROUP BY information_schema.tables.table_name,schema_tables.db_table;
There's unnecessary ) here: AS schema_tables) Should be before AS