Converting SQL with subselect in select to HQL - java

I have the following SQL that I am having problems converting to HQL. A NPE is getting thrown -- which I think has something to do with the SUM function. Also, I'd like to sort on the subselect alias -- is this possible?
SQL (subselect):
SELECT q.title, q.author_id,
(SELECT IFNULL(SUM(IF(vote_up=true,1,-1)), 0)
FROM vote WHERE question_id = q.id) AS votecount
FROM question q ORDER BY votecount DESC
HQL (not working)
SELECT q,
(SELECT COALESCE(SUM(IF(v.voteUp=true,1,-1)), 0)
FROM Vote v WHERE v.question = q) AS votecount
FROM Question AS q
LEFT JOIN q.author u
LEFT JOIN u.blockedUsers ub
WHERE q.dateCreated BETWEEN :week AND :now
AND u.id NOT IN (
SELECT ub.blocked FROM UserBlock AS ub WHERE ub.blocker = :loggedInUser
)
AND (u.blockedUsers IS EMPTY OR ub.blocked != :loggedInUser)
ORDER BY votecount DESC

Here is the working HQL if anyone is interested:
SELECT q,
(SELECT COALESCE(SUM(CASE v.voteUp WHEN true THEN 1 ELSE -1 END), 0)
FROM Vote v WHERE v.question = q) AS votecount
FROM Question AS q
LEFT JOIN q.author u
LEFT JOIN u.blockedUsers ub
WHERE q.dateCreated BETWEEN :week AND :now
AND u.id NOT IN (
SELECT ub.blocked FROM UserBlock AS ub WHERE ub.blocker =:loggedInUser
)
AND (u.blockedUsers IS EMPTY OR ub.blocked !=:loggedInUser)
ORDER BY col_1_0_ DESC
Notice the ORDER BY col_1_0_
There is an open issue with Hibernate -- it does not correctly parse aliases and since the aliases are renamed in the query, an error will be thrown. So, col_1_0_ is basically a workaround --it's the name Hibernate generates.
See issue:
http://opensource.atlassian.com/projects/hibernate/browse/HHH-892

Related

How to select multiple columns in subquery using JPA Criteria

I am trying to write the below query using JPA criteria but I am not able to select the multiple columns in a subquery.
SELECT a.id, a.firstName, a.lastName
FROM PORTRAIT a
JOIN (SELECT firstName, lastName
FROM PORTRAIT
GROUP BY firstName, lastName
HAVING count(id) > 1 ) b
ON b.firstName = a.firstName
AND b.lastName = a.lastName
ORDER BY a.lastName asc
or
SELECT a.id, a.FIRSTNAME, a.LASTNAME
FROM PORTRAIT a where exists (
SELECT b.firstName, b.lastName
FROM PORTRAIT b
WHERE b.firstName = a.firstName
AND b.lastName = a.lastName
GROUP BY b.firstName, b.lastName
HAVING count(b.id) > 1
)
I stuck in the middle of my implementation below where I am not able to find out how to select multiple columns in the subquery. Please see my comment in the code (at 3rd line).
Subquery<PortraitVO> portraitSubQuery = criteriaQuery.subquery(PortraitVO.class);
Root<PortraitVO> portraitRoot = portraitSubQuery.from(PortraitVO.class);
portraitSubQuery.select(portraitRoot); // Here I want to select multiple columns
portraitSubQuery.where(criteriaBuilder.and(criteriaBuilder.equal(portraitRoot.get(RestServiceConstants.FIRST_NAME), root.get(RestServiceConstants.FIRST_NAME)), criteriaBuilder.equal(portraitRoot.get(RestServiceConstants.LAST_NAME), root.get(RestServiceConstants.LAST_NAME))));
List<String> columnNames = new ArrayList<String>();
columnNames.add(RestServiceConstants.FIRST_NAME);
columnNames.add(RestServiceConstants.LAST_NAME);
List<Expression<?>> columnNamesExpression = columnNames.stream().map(x -> portraitRoot.get(x))
.collect(Collectors.toList());
portraitSubQuery.groupBy(columnNamesExpression);
portraitSubQuery.having(criteriaBuilder.gt(criteriaBuilder.count(portraitRoot), 1));
Please help me with this problem.
It doesn't make any sense to select two columns in a subselect used inside an exists. And I'm not sure if it is legal.
Just use one of the columns or a literal.
JPA does not allow to select multiple expressions in a subquery and in fact, selecting anything in a subquery that is wrapped with exists is pointless. Just select a constant value e.g. the integer 1.

SQL server query returns but function does not

In my Java based web project, I have made one recursive query as below which runs perfectly fine and returns list of ids.
WITH treeResult(id) AS
(SELECT pt.id FROM myschema.art_artwork_tree AS pt WHERE pt.id in
(select node_id from myschema.art_brand_user_mapping where emp_id = $1)
UNION ALL
SELECT pa.id FROM treeResult AS p, myschema.art_artwork_tree AS pa
WHERE pa.parent_node = p.id and pa.site_id = $2) SELECT id FROM treeResult AS n
);
Now, I want to use it in JPQL query. So, I have made function as below.
USE [darshandb]
GO
DROP FUNCTION [dbo].[testfunction]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[testfunction] (#empId INT,#siteId INT)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
(
WITH treeResult(id) AS
(SELECT pt.id FROM myschema.art_artwork_tree AS pt WHERE pt.id in
(select node_id from myschema.art_brand_user_mapping where emp_id = $1)
UNION ALL
SELECT pa.id FROM treeResult AS p, myschema.art_artwork_tree AS pa
WHERE pa.parent_node = p.id and pa.site_id = $2) SELECT id FROM treeResult AS n
);
GO
When I am trying to execute function, it does not return any value.
SELECT * FROM [dbo].[testfunction] (4,3);
Please help me, what I have done wrong.
I think your problem is the use of $1 and $2 in your function query. Just use the original parameter names in your table valued function.
So, replace $1 by #empId and $2 by #siteId in your user defined function.

Join with subquery

I have 4 tables
1. members(id, name, milk_no, ...)
2. collections(id, member_id, amount, date, ...)
3. credit_payment_transaction(id, member_id, amount, date, ...)
4. deductions(id, member_id, amount, date, ...)
I am trying to come up with a query that will return for each member.
milk_no | totalDeduction | totalStore | totalCollection
The result should return only member with atleast one of totalDeduction | totalStore | totalCollection
This is what i have come up with
SELECT members.milk_no, memberCollections.totalCollection, stores.totalStore, memberDeductions.totalDeduction
FROM members
LEFT JOIN
(SELECT SUM(amount) AS totalCollection, member_id
FROM collections
GROUP BY member_id) AS memberCollections
ON memberCollections.member_id = members.id
LEFT JOIN
(SELECT SUM(amount) AS totalStore, member_id
FROM credit_payment_transaction
GROUP BY member_id) AS stores
ON stores.member_id = members.id
LEFT JOIN
(SELECT SUM(amount) AS totalDeduction, member_id
FROM deductions
GROUP BY member_id) AS memberDeductions
ON memberDeductions.member_id = members.id
The above query return this
The problem with this result is, it includes unwanted data(the ones with 3 nulls). When i change to RIGHT JOIN no result is returned at all.
Simply add a WHERE clause at the bottom of your query:
WHERE totalCollection IS NOT NULL OR totalStore IS NOT NULL OR totalDeduction IS NOT NULL
You can simplify your query like this:
SELECT m.id, SUM(c.amount) AS totalCollection, SUM(cpt.amount) AS totalStore, SUM(d.amount) AS totalDeduction
FROM members m
LEFT JOIN collections c ON m.id = c.member_id
LEFT JOIN credit_payment_transaction cpt ON m.id = cpt.member_id
LEFT JOIN deductions d ON m.id = d.member_id
GROUP BY m.id
HAVING SUM(c.amount) > 0 OR SUM(cpt.amount) > 0 OR SUM(d.amount) > 0
Also this query will eliminate the member duplications

How to query for number of records in select with "group by" clause in JPA/EclipseLink?

Suppose you have a following JPA query:
select car.year, car.month, count(car) from Car car group by car.year, car.month
Before we query for results, we need to know how many records this query will return (for pagination, UI and so on). In other words we need something like this:
select count(*) from
(select car.year, car.month, count(car)
from Car car group by car.year)
But JPA/EclipseLink does not support subqueries in "from" clause. It there a way around it?
(Of course you can use plain SQL and native queries, but this is not an option for us)
A portable JPA solution:
select count(*) from Car c where c.id in
(select MIN(car.id) from Car car group by car.year, car.month)
You could also go with something like:
select COUNT(DISTINCT CONCAT(car.year, "#", car.month)) from car
but I expect this to be less performant due to operations with textual values.
What about:
select count(distinct car.year) from car
I have another approach to solve this issue . by using my approach you don't need to know the no of rows this query is going to return.
here it is your solution :-
you going to need two variables
1) pageNo (your page no should be 1 for first request to data base and proceeding request it should be incremental like 2 ,3 , 4 5 ).
2) pageSize.
int start = 0;
if(pageNo!=null && pageSize!=null){
start = (pageNo-1) * pageSize;
}else{
pageSize = StaticVariable.MAX_PAGE_SIZE; // default page size if page no and page size are missing
}
your query
em.createquery("your query ")
.setfirstResult(start)
.setMaxResult(pageSize)
.getResultList();
As #chris pointed out EclipseLink supports subqueries. But the subquery can't be the first one in the from-clause.
So I came up with the following workaround which is working:
select count(1) from Dual dual,
(select car.year, car.month, count(car)
from Car car group by car.year) data
count(1) is important as count(data) would not work
You have to add an entity Dual (If your database does not have a DUAL table, create one with just one record.)
This works but I still consider it a workaround that would only work if you allowed to create the DUAL table.
Simply you can use setFirstResult and setMaxResult to set record bound for query ,also use size of list to return count of records that query runs. like this :
Query query = em.createQuery("SELECT d.name, COUNT(t) FROM Department d JOIN
d.teachers t GROUP BY d.name");
//query.setFirstResult(5);
//query.setMaxResult(15); this will return 10 (from 5 to 15) record after query executed.
List<Object[]> results = query.getResultList();
for (int i = 0; i < results.size(); i++) {
Object[] arr = results.get(i);
for (int j = 0; j < arr.length; j++) {
System.out.print(arr[j] + " ");
}
System.out.println();
}
-----Updated Section------
JPA does not support sub-selects in the FROM clause but EclipseLink 2.4 current milestones builds does have this support.
See, http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/JPQL#Sub-selects_in_FROM_clause
You can probably rewrite the query with just normal joins though.
Maybe,
Select a, size(a.bs) from A a
or
Select a, count(b) from A a join a.bs b group by a
I hope this helps you.

SQL Azure and JAVA do not want to run query

I have the following statement in JAVA:
String query = "SELECT H.Name as Name, R.RacesWon as Race FROM Horses AS H, Conditions AS C, Races AS R"+ "WHERE H.ID = R.HorseID AND C.ID = R.ConditionID";
I have connection to a SQL Azure database with 3 tables Horses, Conditions and Races.
I just made this join and I've got this exception:
com.microsoft.sqlserver.jdbc.SQLServerException: Incorrect syntax near 'H'.
I do not know why and I have tried every possibility with Horses, Races and Conditions instead of H,C,R with AS , without AS without success.
Furthermore I have run :
String query = "SELECT Name FROM Horses WHERE ID = 1";
and was OK.
Any suggestions?
You have some concatenation issues. As #lrb notes in the comments:
R"+ "WHERE
Would equal RWHERE which would cause an error.
Also use SQL92 syntax instead of SQL89 with inner join
Instead try:
String query = "SELECT
H.Name as Name,
R.RacesWon as Race
FROM Conditions as C
inner join Races AS R on C.ID = R.ConditionID
inner join Horses AS H on R.HorseID = H.ID";

Categories