how to select group by LEFT(t.field, 6) in HQL? - java

Clean SQL syntax (MySQL) allows to select with "group by LEFT(...)"
Example:
SELECT * FROM `table` t
WHERE t.field1 = 777 AND t.field2 = 0
GROUP BY LEFT(t.field3, 6)
HAVING COUNT(*) = 4;
But if I try to use HQL:
" select t from TableEntity t " +
" where t.field1 = :field_1 and t.field2 = :field_2 " +
" group by LEFT(t.field3, 6) " +
" having COUNT(*) = :other_condition "
I got an exeption
org.hibernate.hql.ast.QuerySyntaxException: unexpected token: LEFT near line 1, column 12 [
Is it possible to use HQL in this case?

I don't think you can do what you want, because MySQL extends the group by -- an extension not generally supported by other databases. You can do something like this:
SELECT substring(t.field3, 1, 6), count(*) as cnt
FROM `table` t
WHERE t.field1 = 777 AND t.field2 = 0
GROUP BY substring(t.field3, 1, 6)
HAVING COUNT(*) = 4;
Note that the unaggregated columns in the group by are also in the select.

Related

QuerySyntaxException: unexpected token: FROM

I'm trying to write a query that will UPDATE a table generated in hibernate, with the result of another hibernate generated table.
I've written this query in ssms, in ssms it runs great.
UPDATE DMVOUCHER set VOUCHERPARTYID = EXTERNALID
from
(
select
EXTERNALID
,row_number() over (partition by dv.VoucherUid order by dc.PRIMARYCOMPANY desc, dc.ExternalId) row_num
,dv.VoucherUid as vUid from DMVOUCHER dv
join DMCOMPANY dc on dv.VOUCHERPARTYID = dc.REGISTRATION
where VOUCHERTYPE = 'R03') as t1
where t1.row_num = 1
and VoucherUid = vUid
and VOUCHERTYPE = 'R03'
row_num is to make it prefer the result if primaryCompany is equal to 1 (PrimaryCompany is a boolean), if that doesn't exist then it can do it based on any externalId. in my query I just prefer the lowest externalId
however the problem then comes when I'm trying to execute this in my application. here I am trying to execute the equivelant string which can be seen below:
String queryString =
"UPDATE DMVOUCHER \n"
+ "SET VOUCHERPARTYID = EXTERNALID \n"
+ "FROM ( \n"
+ " SELECT EXTERNALID \n"
+ " , row_number() over (partition by dv.VoucherUid order by dc.PRIMARYCOMPANY desc, dc.ExternalId) row_num \n"
+ " , dv.VoucherUid as vUid \n"
+ " FROM DMVOUCHER dv \n"
+ " JOIN DMCOMPANY dc ON dv.VOUCHERPARTYID = dc.REGISTRATION \n"
+ " WHERE VOUCHERTYPE = 'R03') as t1 \n"
+ "WHERE t1.row_num = 1 \n"
+ " AND VoucherUid = vUid \n"
+ " AND VOUCHERTYPE = 'R03'";
however I then end up with this error message:
2021-06-21 13:23:27.173. Error: TopDmCompanyGtiNumberEntityAfterMapper failed: QuerySyntaxException: unexpected token: FROM near line 3, column 1 [UPDATE DMVOUCHER
SET VOUCHERPARTYID = EXTERNALID
FROM (
SELECT EXTERNALID
, row_number() over (partition by dv.VoucherUid order by dc.PRIMARYCOMPANY desc, dc.ExternalId) row_num
, dv.VoucherUid as vUid
FROM DMVOUCHER dv
JOIN DMCOMPANY dc ON dv.VOUCHERPARTYID = dc.REGISTRATION
WHERE VOUCHERTYPE = 'R03') as t1
WHERE t1.row_num = 1
AND VoucherUid = vUid
AND VOUCHERTYPE = 'R03']
org.hibernate.hql.internal.ast.QuerySyntaxException.convert(QuerySyntaxException.java:74)
org.hibernate.hql.internal.ast.ErrorTracker.throwQueryException(ErrorTracker.java:93)
org.hibernate.hql.internal.ast.QueryTranslatorImpl.parse(QueryTranslatorImpl.java:301)
org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:189)
org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:144)
org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:113)
org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:73)
org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:162)
org.hibernate.internal.AbstractSharedSessionContract.getQueryPlan(AbstractSharedSessionContract.java:604)
org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:716)
org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:113)
com.schantz.generallife.dm.logic.writer.DmUtil.executeQuery(DmUtil.java:16)
com.schantz.generallife.dm.logic.datamart.DatamartWriter.handleSynchronousRequest(DatamartWriter.java:211)
com.schantz.generallife.dm.logic.datamart.DatamartWriter.write(DatamartWriter.java:151)
com.schantz.generallife.dm.logic.datamart.DatamartWriter$1.execute(DatamartWriter.java:107)
com.schantz.foundation.config.hibernate.AbstractDatabaseTargetThreadLogic$DatabaseTargetThread.execute(AbstractDatabaseTargetThreadLogic.java:170)
com.schantz.foundation.config.hibernate.AbstractDatabaseTargetThreadLogic$DatabaseTargetThread.run(AbstractDatabaseTargetThreadLogic.java:159)
java.base/java.lang.Thread.run(Thread.java:834)
I've tried to look around on stackoverflow for similiar errors, however I havn't been able to identify any queries like mine. or where the problem is that the unexpected token is the FROM
I think a possible issue is that ssms uses a different sql language than my application does. I must admit I don't know the actual sql language either that my ssms uses or my application does. which also makes debugging the problem a bit harder. so if you are able to identify it based on what I've written, that information would also be appriciated.
edit following the method call where I execute my queryString, it reaches a new method which calls another method. doing that a few times I finally get this method:
public static Integer executeQuery(PersistenceManager persistenceManager, Class<? extends AbstractEntity> clazz, String queryString, Map<String, Object> parameters) {
Session session = (Session) ((PersistenceManagerImpl) persistenceManager).getTransactionalEntityManager().getDelegate();
Query<?> query = session.createQuery(queryString);
for (Entry<String, Object> entry : parameters.entrySet()) {
query.setParameter(entry.getKey(), entry.getValue());
}
return query.executeUpdate();
}
where executeUpdate() is from jakarta.persistence-api-2.2.3.jar
--edit--
I messed around a bit and tried to use the way it would written in mysql instead. therefore I now get this error message instead.
2021-06-22 11:44:10.496. Error: TopDmCompanyGtiNumberEntityAfterMapper failed: QuerySyntaxException: expecting IDENT, found '(' near line 2, column 1 [UPDATE
(SELECT EXTERNALID
, row_number() over (partition by dv.VoucherUid order by dc.PRIMARYCOMPANY desc, dc.ExternalId) row_num
, dv.VoucherUid as vUid
FROM DMVOUCHER dv
JOIN DMCOMPANY dc ON dv.VOUCHERPARTYID = dc.REGISTRATION
WHERE VOUCHERTYPE = 'R03') as t1 SET VOUCHERPARTYID = EXTERNALID
WHERE t1.row_num = 1
AND VoucherUid = vUid
AND VOUCHERTYPE = 'R03']
org.hibernate.hql.internal.ast.QuerySyntaxException.convert(QuerySyntaxException.java:74)
org.hibernate.hql.internal.ast.ErrorTracker.throwQueryException(ErrorTracker.java:93)
org.hibernate.hql.internal.ast.QueryTranslatorImpl.parse(QueryTranslatorImpl.java:301)
org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:189)
org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:144)
org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:113)
org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:73)
org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:162)
org.hibernate.internal.AbstractSharedSessionContract.getQueryPlan(AbstractSharedSessionContract.java:604)
org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:716)
org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:113)
com.schantz.generallife.dm.logic.writer.DmUtil.executeQuery(DmUtil.java:16)
com.schantz.generallife.dm.logic.datamart.DatamartWriter.handleSynchronousRequest(DatamartWriter.java:211)
com.schantz.generallife.dm.logic.datamart.DatamartWriter.write(DatamartWriter.java:151)
com.schantz.generallife.dm.logic.datamart.DatamartWriter$1.execute(DatamartWriter.java:107)
com.schantz.foundation.config.hibernate.AbstractDatabaseTargetThreadLogic$DatabaseTargetThread.execute(AbstractDatabaseTargetThreadLogic.java:170)
com.schantz.foundation.config.hibernate.AbstractDatabaseTargetThreadLogic$DatabaseTargetThread.run(AbstractDatabaseTargetThreadLogic.java:159)
java.base/java.lang.Thread.run(Thread.java:834)
I believe it makes the problem a bit clearer, I need to covert my query query so its in hql instead. I will answer the post if I figure out how to do that, as I'm not so familiar with hql
the problem was indeed that the queryString was supposed to be in hql. I've rewritten the queryString so it now works, it now looks like this:
String queryString =
"UPDATE DmVoucher SET voucherPartyId = (SELECT externalId \n"
+ " FROM DmCompany dc \n"
+ " WHERE voucherType = 'R03' \n"
+ "And dc.primaryCompany = CASE WHEN (SELECT count(dcp) FROM DmCompany dcp WHERE primaryCompany = 1 and voucherPartyId = dcp.registration) > 0 THEN 1 else 0 END\n"
+ "and voucherPartyId = dc.registration)\n";

unexpected end of subtree JPQL

I have a query which count a given code from 4 tables. I tested this query first in postgresql, it worked as I expected so I tried to translate it to JPQL and I got this error :
java.lang.IllegalArgumentException:
org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected end of
subtree [ select ((select count(*) from *.Record r where
r.codeCampaign =?1 ) + (select count(*) from *.AccountAssociationAction aaa where aaa.codeCampaign = ?1) +
(select count(*) from *.CampaignPayment cp where cp.pk.campaignCode = ?1) + (select count(*) from
*.ActionPeriodDepartment apd
where apd.id.codeCampaign = ?1))]
I can't figure out what is wrong and what does Hibernate mean by "unexpected end of subtree"
The postgresql query :
select (select count(*) from account_association_action aaa where
aaa.code_campaign = 'CAMP01') + (select count(*) from _campaign_payment cp
where cp.campaign_code = 'CAMP01') + (select count(*) from record r where
r.code_campaign ='CAMP01') + (select count(*) from action_period_department apd
where apd.code_campaign = 'CAMP01');
the JPQL query :
#Query(value=" select (select count(*) from Record r where r.codeCampaign =?1 ) + " +
" (select count(*) from AccountAssociationAction aaa where aaa.codeCampaign = ?1) +" +
" (select count(*) from CampaignPayment cp where cp.pk.campaignCode = ?1) +" +
" (select count(*) from ActionPeriodDepartment apd where apd.id.codeCampaign = ?1)")
int countCampaignCodeUses(String campaignCode);
It looks like you need to add nativeQuery=true to #Query annotation otherwise JPA is failing to make sense of a query without from

MySQL JDBC Fail calculating row number

I have this select that works every time running in workbench but fails sometimes for the same arguments over jdbc. The problem is that sometimes, over JDBC, 'pos' value returns null. I Think that, for some reason, the #p as not started, but dont know how to fix.
SELECT t1.wId, t1.twId, t1.name, t1.timeout, t1.pos
FROM (
SELECT w.id AS wId, tw2.id AS twId, w.name AS name, tw2.timeout AS timeout, #p:=#p+1 AS pos
FROM timeout_workqueue tw1
INNER JOIN timeout_workqueue tw2
ON tw1.workqueue_id = tw2.workqueue_id
INNER JOIN workqueue w
ON tw1.workqueue_id = w.id
WHERE tw1.id = ?
ORDER BY tw2.id) t1, (SELECT #p:=1) c
WHERE t1.twId = ?;
The whole Java Code Are:
public TimeoutWorkqueueView getTimeoutWorkqueueView(Integer id) {
String sql = "SELECT t1.wId, t1.twId, t1.name, t1.timeout, t1.pos"
+ " FROM ("
+ " SELECT w.id AS wId, tw2.id AS twId, w.name AS name, tw2.timeout AS timeout, #p:=#p+1 AS pos"
+ " FROM timeout_workqueue tw1"
+ " INNER JOIN timeout_workqueue tw2"
+ " ON tw1.workqueue_id = tw2.workqueue_id"
+ " INNER JOIN workqueue w"
+ " ON tw1.workqueue_id = w.id"
+ " WHERE tw1.id = ?"
+ " ORDER BY tw2.id) t1, (SELECT #p:=1) c"
+ " WHERE t1.twId = ?";
return (TimeoutWorkqueueView) getJdbcTemplate().queryForObject(sql, new BeanPropertyRowMapper(TimeoutWorkqueueView.class), id, id);
}
Ok so the problem i can see here (please verify) is that you think you are running the query with parameter set to 1 to begin with.
However if you set #p = 1 -> this #p:=#p+1 will not evaluate to 1 any more.
Also assuming you have 20 rows, you run this query 20 times but on the last run it will return null because pos will be 21 and this does not exists.

HQL - Delete with JOIN error

Im trying to execute an HQL delete with join.. After soon searching I found that I need to create a query just like suggested HERE:
http://dasunhegoda.com/1093-you-cant-specify-target-table-table_name-for-update-in-from-clause/104/
This is my query:
dao.executeByHql(
"DELETE FROM FinalGradeResult e WHERE e.id IN "
+ "( SELECT id FROM "
+ "( SELECT x FROM FinalGradeResult x "
+ "where x.student.id = :studentId "
+ " AND x.classDiscipline IN " +
+ "(SELECT cd from ClassDiscipline cd "
+ " where cd.clazz.id = :clazzId ) ) as X )",
new HqlParameter("studentId", student.getId()),
new HqlParameter("clazzId", from.getId()));
But I keep getting this error:
org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token:( near line 1, column 94 [DELETE FROM xxxxxxxxxxxx.entity.FinalGradeResult e WHERE e.id IN ( SELECT id FROM ( SELECT x FROM xxxxxxxxxxxxxxx.entity.FinalGradeResult x where x.student.id = :studentId AND x.classDiscipline IN (SELECT cd from xxxxxxxxxxxxxxxx.entity.ClassDiscipline cd where cd.clazz.id = :clazzId ) ) as X )]
The error points out that the second ( is wrong, the one right after "SELECT id FROM"
EDIT I ve tried like this and same error occours:
dao.executeByHql(
"DELETE FROM FinalGradeResult e WHERE e.id IN "
+ "( SELECT id FROM "
+ "( SELECT x FROM FinalGradeResult x "
+ " where x.student.id = :studentId "
+ " AND x.classDiscipline.clazz.id = :clazzId )"
+ " as X )",
EDIT 2 : The query like this doesnt work because of the problem described on the link I've posted in this question:
dao.executeByHql(
"DELETE FROM FinalGradeResult e WHERE e.id IN " + "( SELECT x.id FROM FinalGradeResult as x "
+ " where x.student.id = :studentId " + " AND x.classDiscipline.clazz.id = :clazzId )",
new HqlParameter("studentId", student.getId()), new HqlParameter("clazzId", from.getId()));
The error is:
Caused by: java.sql.SQLException: You can't specify target table 'tb_final_grade_result' for update in FROM clause
SOLUTION
After trying everything out here's our conclusion:
We cant have only one query directly because we cant have joins on DELETE.
We cant have only 2 queries (one subquery) because we have a MYSQL BUG (described on the link provided)
We cant have 3 queries (2 subqueries) because we cant have a subquery in the FROM clause. That's why our second query doesn´t work (select * from (select ...)) is invalid.
So I decided to use NativeSQL to solve the problem:
dao.executeBySQL(
" delete from tb_final_grade_result where id in "
+ " (select * from ( select finalgrade1_.id from tb_final_grade_result finalgrade1_ cross join tb_class_discipline classdisci2_ "
+ " where finalgrade1_.id_class_discipline=classdisci2_.id and finalgrade1_.id_student= :studentId and classdisci2_.id_class= :clazzId ) as tmp )",
new HqlParameter("studentId", student.getId()), new HqlParameter("clazzId", from.getId()));
Special thanks to #scaisEdge
Why do you need to use in (select on the same table that you are deleting from? Can't you just put the condition in the where clause?
DELETE FROM FinalGradeResult e WHERE e.student.id = :studentId " + " AND e.classDiscipline.clazz.id = :clazzId )",
new HqlParameter("studentId", student.getId()), new HqlParameter("clazzId", from.getId()));
Also, I'm not sure what you are referring to with the parameter classDiscipline.clazz.id? Is classDiscipline some other entity with a field named clazz that is yet another entity? That's what the query seems to be saying.
The problem with your original query is that you are not allowed you have inner selects anywhere but in select and where clauses. Because HQL operates in terms of entities and not tables or columns, selecting from subsets of tables doesn't make sense. See here:
Note that HQL subqueries can occur only in the select or where clauses.
We had a similar issue in our project and I struggled for awhile to find a single query solution, but I'm afraid with MySQL it isn't possible due to HQL's language design.
The best I came up with was to first fetch the ids as a list, and then pass that list as a list parameter to your update. For you it might be something like:
Query idQuery = createQuery("select id from FinalGradeResult gr where gr.student.id = :studentId AND gr.classDiscipline IN (SELECT cd from ClassDiscipline cd where cd.clazz.id = :clazzId"));
//add parameters
List<Number> ids = query.list();
Query entityQuery = createQuery("delete from FinalGradeResult where id in (:ids)");
entityQuery.setParameterList("ids", ids);
query.executeUpdate()
could be the position of as X is wrong
and you are repeting x instead of column name ( set ***column_name*** with the proper column name)
"DELETE FROM FinalGradeResult e WHERE e.id IN "
+ "( SELECT id FROM
( SELECT ***column_name *** FROM FinalGradeResult x where x.student.id = :studentId AND x.classDiscipline IN
(SELECT cd from ClassDiscipline cd where cd.clazz.id = :clazzId ) as X) )",
If the sibquery don't work in hibernate then try with a simple query with join
DELETE FROM FinalGradeResult e
WHERE e.id in (
SELECT id FROM FinalGradeResult x
JOIN ClassDiscipline cd ON ( cd.cd.clazz.id = :clazzId
and x.classDiscipline = cd.ClassDiscipline )
WHERE x.student.id = :studentId);
SOLUTION
After trying everything out here's our conclusion:
We cant have only one query directly because we cant have joins on DELETE.
We cant have only 2 queries (one subquery) because we have a MYSQL BUG (described on the link provided)
We cant have 3 queries (2 subqueries) because we cant have a subquery in the FROM clause. That's why our second query doesn´t work (select * from (select ...)) is invalid.
So I decided to use NativeSQL to solve the problem:
dao.executeBySQL(
" delete from tb_final_grade_result where id in "
+ " (select * from ( select finalgrade1_.id from tb_final_grade_result finalgrade1_ cross join tb_class_discipline classdisci2_ "
+ " where finalgrade1_.id_class_discipline=classdisci2_.id and finalgrade1_.id_student= :studentId and classdisci2_.id_class= :clazzId ) as tmp )",
new HqlParameter("studentId", student.getId()), new HqlParameter("clazzId", from.getId()));

How can I get the alias name for my ResultSet from a Subquery?

I have a problem with getting the alias of the columnnames for my ResultSet.
I made a subquery, where i use the alias function(MAX(...) in SQL) but everytime I execute the Statement, I get java.sql.SQLException because the column name is not valid. And I use the current alias where I call the getString - Function of my ResultSet.
This is my SQL-Statement in Eclipse:
String sql = "SELECT a.steelgrade, a.prod_order_id, a.prod_order_item_pos, "
+"a.prod_order_version, a.strip_thickn_aim, a.strip_width_aim, "
+"a.customer, a.order_weight_plan, b.grund_kommlos, b.coil_weight "
+"FROM (SELECT prod_order_id, prod_order_item_pos, "
+ "MAX (prod_order_version) AS max_version "
+ "FROM production_order "
And in the ResultSet while.next()-Loop:
prod_order_version = AuftraegeProduction.getString("max_version");
This is the whole SQL-Statement( in the Database it works fine!):
SELECT a.steelgrade, a.prod_order_id, a.prod_order_item_pos,
a.prod_order_version, a.strip_thickn_aim, a.strip_width_aim,
a.customer, a.order_weight_plan, b.grund_kommlos, b.coil_weight
FROM (SELECT prod_order_id, prod_order_item_pos,
MAX (prod_order_version) AS max_version
FROM production_order
GROUP BY prod_order_id, prod_order_item_pos) c
JOIN
production_order a
ON a.prod_order_id = c.prod_order_id
AND a.prod_order_item_pos = c.prod_order_item_pos
AND a.prod_order_version = c.max_version
JOIN pps_plan_slab b
ON b.prod_order_id = c.prod_order_id
AND b.prod_order_item_pos = c.prod_order_item_pos
AND b.prod_order_version = c.max_version
WHERE a.strip_thickn_aim > 1.78
AND a.strip_thickn_aim < 3.26
AND a.steelgrade = 'M4R51'
AND a.prod_order_id NOT BETWEEN '0999551' AND '0999599'
AND a.strip_width_aim BETWEEN 1126 AND 1166
AND NVL (a.order_weight_plan, 0) > 0
AND a.order_weight_plan >= b.coil_weight
ORDER BY prod_order_id ASC
Anyone have a suggestion?
Maurice
when using aggregate functions max(),min(),sum(),... you must use group by clause clause.

Categories

Resources