How to select count(*) from many to many in spring data jpa? - java

I have two tables (for example posts:tags) with M:N relationship with standard middle table.
I want to select all post ids with the count of tags per each post with spring data jpa.
Here is what i did:
SELECT p.id, count(t) as total FROM post p join p.tags t;
However this is not returning the correct result.

Fixed it, adding group by did the trick.
SELECT p.id, count(t) as total
FROM post p join p.tags t
GROUP BY p.id;

Related

Select statements, Joins, and Repurpose Query in SQL

I have a constant set of users that I want to filter out and apply to each query to look at what they are doing on an app. I have been poking and probing around here to get a better sense of how to do this, but it is still unclear to me. I am a newbie with SQL statements and JAVA. Any help, particularly explanation is highly welcomed.
I have been trying to figure it out using these two articles to no avail: 1 & 2; these examples show how you can create a query and use it in another query, which is what I am trying to do.
Someone mentions a wrap (see ref. 1) which is what i attempt to do:
Sure, wrap your two set operations in outer queries with selects that include fixed columns called Source, i.e. SELECT
'horrible_query_1' AS Source, * and SELECT 'ugly_query_2' AS Source,
*.
That way your UNION will give you all the columns from your query plus the source identifier.
My question: is it possible to repurpose these queries into separate queries without having to do joins with the variable? I don't understand how that works (I'm not sure I am saying it right either).
This is the User group and filter I want to repurpose across all queries:
<select id="get_num_x_users" resultClass="java.lang.Integer">
select count(distinct user_id) from positions sp
join position_definitions sd on sp.position_id = sd.id
where sd.position like '%catcher%'
or lower(sd.position) like '%pitcher%'
or lower(sd.position) like '%dh%';
</select>
Repurpose into this query (among a few others):
<select id="get_u_counts" resultClass="java.lang.Integer">
SELECT COUNT(DISTINCT u.id)
FROM searches s
JOIN users u
ON s.user_id = u.id
WHERE search_date > DATE_SUB(curdate(), INTERVAL 3 MONTH)
AND LENGTH(search_term) > 0
AND search_term NOT LIKE ' LIC: SPEC: %'
AND u.type != 'coach';
</select>
My attempt (That does not work when I am in mysql database):
with get_x_users as (
select count(distinct user_id) from positions sp
join position_definitions sd on sp.position_id = sd.id
where sd.position like '%catcher%'
or lower(sd.position) like '%pitcher%'
or lower(sd.position) like '%dh%';),
(SELECT COUNT(get_x_users)
FROM searches s
JOIN users u
ON s.user_id = u.id
AND get_x_users
WHERE search_date > DATE_SUB(curdate(), INTERVAL 3 MONTH)
AND LENGTH(search_term) > 0
AND u.type != 'coach');
In SQL we can join tables together using a common field. For instance, if you have a user_id field in two different tables you can match the records using that common field. You can also create a join that gives you all the records in one table and only those that match in the second table, that's what LEFT JOIN does.
Using this principle, you do a little reverse logic and create a query which gives you all the records from your search query (ExcludeQuery) and join it to the users whom you want to exclude. This will give you a list of records with and without matches to the excluded users. What you do then is use a where to only include the records that haven't matched an excluded user, WHERE ExcludeQuery.user_id IS NULL.
SELECT s.*
FROM searches s
JOIN users u
ON s.user_id = u.id
WHERE search_date > DATE_SUB(curdate(), INTERVAL 3 MONTH)
AND LENGTH(search_term) > 0 AND u.type != 'coach'
LEFT JOIN
(select user_id from positions sp
join position_definitions sd on sp.position_id = sd.id
where sd.position like '%catcher%'
or lower(sd.position) like '%pitcher%'
or lower(sd.position) like '%dh%') AS ExcludeQuery
ON ExcludeQuery.user_id=s.user_id
WHERE ExcludeQuery.user_id IS NULL

Convert SQL to hibernate query using criteria and/or projection (subqueries)

Could anyone please help to convert this SQL Query to HQL (using criteria and projection) to have in result Set<topic> topics.
SELECT t.id, t.title, COUNT(p.id) as count, MAX(p.created_on) as `latest`
FROM topics t
JOIN posts p
ON t.id = p.topic_id
GROUP BY t.id
ORDER BY count ASC;
My problem is that Hibernate is fetching all data connected to one of columns of the topic table (connected table is users) - and query takes to much time and unnecessary selects and joins.
Tried to use projections but couldn't.
Thanks in advance for any help!
From your code,
SELECT t.id, t.title, COUNT(p.id) as count, MAX(p.created_on) as `latest`
FROM topics t
JOIN posts p
ON t.id = p.topic_id
GROUP BY t.id
ORDER BY count ASC;
You can remove JOIN and convert it in WHERE condition. Hope it will save you.
Use this following code
SELECT t.id, t.title, COUNT(p.id) as count, MAX(p.created_on) as `latest`
FROM topics t, posts p
WHERE t.id = p.topic_id
GROUP BY t.id
ORDER BY count ASC;

Hibernate error: unexpected AST node

I need to sort topics by latest post. Could someone please help me with this hibernate query:
unexpected AST node: query
[SELECT t
FROM Topic t
ORDER BY
(SELECT MAX(p.createdOn) FROM Post p WHERE p.topic.id = t.id)
DESC]
What is the issue here?
SELECT topic FROM Post ORDER BY createdOn
I think that this JPQL query should work
select t.id, t.description, max(p.createdOn) as maxCreationTime
from Topic t inner join t.posts p
group by t.id, t.description
order by maxCreationTime
If you want to include topics that have not related posts.
select t.id, t.description, max(p.createdOn) as maxCreationTime
from Topic t left join t.posts p
group by t.id, t.description
order by maxCreationTime
You have to specify the selected fields from Topic in order to make the aggregation function max work. Hope this helps.

How to count all groups returned by a group by query in JPQL?

If I have a group by query
Select e.field1, e.field2...
from Entity w
group by e.field1, e.field2...
How can I count the number of rows not using NativeQuery?
I need to do something like this:
select count(*) from
(Select e.field1, e.field2...
from Entity w
group by e.field1, e.field2...)
Is there any way to do with JPQL?
in oracle you can this hql
Select sum(count(*) - count(*) + 1)
from Entity e
group by e.field1, e.field2...
but dose not work in postgres
Derived tables are not supported in JPQL, so, the best way to do it is to run a native SQL query.
After all, there is a very good reason why both JPA and Hibernate offer support for SQL queries, don't you agree?

HQL/SQL/Criteria to join-match all records in a given list while selecting all fields

I'm trying to write a HQL/Criteria/Native SQL query that will return all Employees that are assigned to a list of Projects. They must be assigned to all Projects in order to be selected.
An acceptable way of achieving this with native SQL can be found in the answer to this question: T-SQL - How to write query to get records that match ALL records in a many to many join:
SELECT e.id
FROM employee e
INNER JOIN proj_assignment a
ON e.id = a.emp_id and a.proj_id IN ([list of project ids])
GROUP BY e.id
HAVING COUNT(*) = [size of list of project ids]
However, I want to select all fields of Employee (e.*). It's not possible to define SQL grouping by all the columns(GROUP BY e.*), DISTINCT should be used instead. Is there a way to use DISTINCT altogether with COUNT(*) to achieve what I want?
I've also tried using HQL to perform this query. The Employee and ProjectAssignment classes don't have an association, so it's not possible to use Criteria to join them. I use a cross join because it's the way to perform a Join without association in HQL. So, my HQL looks like
select emp from Employee emp, ProjectAssignment pa
where emp.id = pa.empId and pa.paId IN :list
group by emp having count(*) = :listSize
However, due to a bug in Hibernate, GROUP BY entity does not work. The SQL it outputs is something like group by (emptable.id).
Subquerying the assignment table for each project (dynamically adding and exists (select 1 from proj_assignment pa where pa.emp_id=e.id and pa.proj_id = [anId]) for each project in the list) is not an acceptable option.
Is there a way to write this query properly, preferrably in HQL (in the end I want a List<Employee>), without modifying mappings and without explicitly selecting all columns in the native SQL ?
EDIT: I'm using Oracle 10g and hibernate-annotations-3.3.1.GA
How about:
select * from employee x where x.id in(
SELECT e.id
FROM employee e
INNER JOIN proj_assignment a
ON e.id = a.emp_id and a.proj_id IN ([list of project ids])
GROUP BY e.id
HAVING COUNT(*) = [size of list of project ids]
)
I've found an alternative way to achieve this in HQL, it's far more inefficient than what I'd like, (and than what is really possible without that nasty bug) but at least it works. It's better than repeating subselects for each project like
and exists (select 1 from project_assignment pa where pa.id = someId and pa.emp_id = e.id)
It consists of performing a self-join subquery in order to find out, for each of the Employees, how many of the projects in the list they are assigned to, and restrict results to only those that are in all of them.
select e
from Employee
where :listSize =
(select distinct count(*)
from Employee e2, ProjectAssignment pa
where
e2.id = pa.id_emp and
e.id = e2.id
and pa.proj_id IN :projectIdList
)

Categories