Join with subquery - java

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

Related

JDBC JOINing multiple child tables to a parent table, how can i know from which table is the resulting row?

Lets suppose i have 3 tables one is parent others are child
| Resource |
______________
| id |
| name |
|author |
_______________
| Movie |
_______________
|id
|lenght
|main actor
________________
| Book |
__________________
|id
|number of pages |
|Pubblisher |
___________________
now what i want to do is joining the three tables together start a rs = stmt.executeQuery(sql)
to retrieve all the data so that the sql String i pass might be
sql= "SELECT * FROM Resource
LEFT JOIN Movie
LEFT JOIN BOOK
USING(id)
then
while(rs.next())
{
//creating different objects with the data base on if its a movie or a book
..
}
how can i know from the join i made from which table the resulting row is from (movie or book) without using checks on attributes like
if(rs.getString("pubblisher").equals(NULL))
//creation of a movie
Thank you very much!
Don't use SELECT *. Instead, you should list the columns you want your query to return; and you can use a CASE expression to implement the logic that checks from which column values were returned:
select
r.id resource_id,
r.name resource_name,
r.author resource_author,
m.length movie_length,
m.main_actor movie_main_actor,
b.number_of_pages book_number_of_pages,
b.publisher book_publisher,
case
when m.id is not null then 'movie'
when b.id is not null then 'book'
else 'none'
end src
from resource r
left join movie m on m.id = r.id
left join book b on b.id = r.id
Here column src will contain either movie, book, or none, depending on which left join did succeed. If it is possible that both left joins would succceed (which does not seem likely given your set-up), then you can adapt the logic:
case
when m.id is not null and b.id is not null then 'both'
when m.id is not null then 'movie'
when b.id is not null then 'book'
else 'none'
end src

SQL: Get a limit of records per entity

I have the following setup (Java/Hibernate/PostgreSQL):
TeamName {
id: Long;
name: String;
team: Team;
....
}
Series {
id: Long;
season: Season;
dateScheduled: Date;
}
SeriesTeam {
id: Long;
series: Series;
team: TeamName;
}
SeriesTeam {
id: Long;
team: TeamName;
}
What I want to do is do a select of the past n series (say 10) or the next series from the current date. Here's what I have so far:
select s.* from series s
inner join series_teams st on st.series_id = s.id
inner join team_names tn on tn.id = st.team_name_id
where tn.id in (:teamIds) and s.date_scheduled < CURRENT_DATE
order by s.date_scheduled desc
But that is going to get me all the prior series for all teams and I will have to use Java to pick out what I want How would I go about doing what I want? Thanks!
EDIT: For example, say I wanted a limit of 10 per team name, and there are 24 teams, I would want max of 240 records returned to me. (assuming 10 exist before current date)
EDIT2: Here is the code that I want for a single team:
select s. from series s
inner join series_teams st on st.series_id = s.id
where st.team_name_id=85 and s.date_scheduled < CURRENT_DATE
order by s.date_scheduled desc
limit 10
I just need to be able to apply this for all the teams....I don't want to make x SQL calls for every team.
I think this will work. The syntax is in mysql and you can try it at that site the structure they have is similar to yours. you can adjust the limit value to change how many from each employee to return sorted by date. Probably add the current date check there too i guess.
Basically I joined all the needed tables together then created a new column that will tell me if that row is one we should return then added that check in the where clause.
https://www.w3schools.com/sql/trysql.asp?filename=trysql_op_in
SELECT e.employeeid, lastname,orderdate, orderdate in (select orderdate from
orders ord where e.employeeid=ord.employeeid order by orderdate limit 2) as
good FROM Employees as e join orders as o on e.employeeid=o.employeeid where
good=1 order by e.employeeid, o.orderdate;
for your case:
select s.id, s.season_id, s.date_scheduled, st.team_name_id,
s.date_scheduled in (
select s2.date_scheduled from series s2
inner join series_teams st2 on st2.series_id = s2.id
inner join team_names tn2 on tn2.id = st2.team_name_id
where tn.id =tn2.id and s2.date_scheduled < CURRENT_DATE
order by s.date_scheduled desc limit 5
) as foo
from series s
inner join series_teams st on st.series_id = s.id
inner join team_names tn on tn.id = st.team_name_id
where tn.id in (:teamIds) and foo = true
order by st.team_name_id, s.date_scheduled desc

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.

JPA/JPQL JOIN Subselect/Subquery

I have trouble to translate some easy SQL statement to JPQL, because of a used subquery, which is not supported by JPQL.
Can someone give me a hint, how to achieve the same result with JPQL or JPA2 Criteria API?
Given (Simplified fake data to demonstrate the problem):
CREATE TABLE person (id integer, name text);
CREATE TABLE phone (id integer, person_id integer, type text, number text);
INSERT INTO person VALUES (1, "John");
INSERT INTO person VALUES (2, "Mike");
INSERT INTO person VALUES (3, "Paul");
INSERT INTO person VALUES (4, "Walter");
INSERT INTO phone VALUES (1, 1, "MOBILE", "+49-123-11111");
INSERT INTO phone VALUES (2, 1, "HOME" , "+49-123-22222");
INSERT INTO phone VALUES (3, 2, "WORK" , "+49-123-33333");
INSERT INTO phone VALUES (4, 4, "MOBILE", "+49-123-44444");
-- Select all from person and their mobile number if possible
-- This query has to be translated to JPQL
SELECT person.name, mobile.number FROM person LEFT JOIN (
SELECT * FROM phone WHERE type = "MOBILE"
) AS mobile ON person.id = mobile.person_id;
Expected result:
| name | number |
|--------|---------------|
| John | +49-123-11111 |
| Mike | |
| Paul | |
| Walter | +49-123-44444 |
Java:
class Person {
String name;
List<Phone> phones;
}
class Phone {
String type;
String number;
}
JPQL (not working as expected :-( ):
SELECT person.name, phone.number FROM Person person
LEFT JOIN person.phones AS phone
WHERE phone.type = "MOBILE"
SELECT person.name, phone.number
FROM Person AS person LEFT JOIN person.phones AS phone
ON phone.type = 'MOBILE'
You can also replace the ON keyword with the hibernate specific WITH:
SELECT person.name, phone.number
FROM Person AS person LEFT JOIN person.phones AS phone
WITH phone.type = 'MOBILE'
First of all according to the Java Persistence Wikibook some JPA providers like EclipseLink and TopLink support sub selects in the FROM clause - although this is not defined in the JPA spec.
In JPA 2.1 you could use LEFT JOIN with ON:
SELECT person.name, phone.number
FROM Person person
LEFT JOIN person.phones AS phone ON phone.person = person AND phone.type = 'MOBILE'
Before JPA 2.1 you could use a case expression:
SELECT person.name,
CASE WHEN (phone.type = 'MOBILE') THEN phone.number ELSE '' END
FROM Person person
LEFT JOIN person.phones AS phone
But this will just wipe all none-mobile phone numbers - so there will be a row for each phone number of a person, even if he/she has more than one phone number that is not a mobile number.
You could use the list aggregation function of your database (like LISTAGG in Oracle), if your JPA provider renders these correctly (Hibernate does in most circumstances). This would make sense for the first two options as well - if a person can have more than one mobile number.
Try this:
SELECT person.name, phone.number
FROM Person person
LEFT JOIN person.phones AS phone
WHERE phone.type IS NULL OR phone.type = "MOBILE"

Converting SQL with subselect in select to HQL

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

Categories