Dynamic attribute name search - java

Consider I have a student table like this:
student_id name address ... school employer
1 Chris 2 John UofJ J Limited
2 Ann 3 Doe UofD D limited
Right now I need to find a list of students who have school = 'UofJ' and employer = 'J Limited'. Easy:
select * from student where school = 'UofJ' and employer = 'J Limited'
However, my reality is the last 2 attributes are stored in student table as columns but in a separate table called student_attribute as rows:
student_attribute_id student_id attribute_name attribute_value
1 1 school UofJ
1 1 company J Limited
1 2 school UofD
1 2 company D Limited
My task is to find a list of student IDs from this student_attribute table still based on school = 'UofJ' and employer = 'J Limited'. How should I do it?
Moreover, I am using Springboot JPS repository to do the query, so I am willing to listen to solution to both a sql way or JPA way.

You can use conditional aggregation to find out which student_id has both the conditions true.
select student_id
from student_attribute
group by student_id
having count(case
when attribute_name = 'school'
and attribute_value = 'UofJ'
then 1
end) > 0
and count(case
when attribute_name = 'company'
and attribute_value = 'J Limited'
then 1
end) > 0
You can then maybe join it with the student table to get the corresponding details.
select s.*
from student s
join (
select student_id
from student_attribute
group by student_id
having count(case
when attribute_name = 'school'
and attribute_value = 'UofJ'
then 1
end) > 0
and count(case
when attribute_name = 'company'
and attribute_value = 'J Limited'
then 1
end) > 0
) a on s.student_id = a.student_id;

Set up a join for each attribute you care about:
select * from student s
join student_attribute school on school.student_id = s.student_id
join student_attribute company on company.student_id = s.student_id
where company.attribute_value='J Limited'
and school.attribute_value='UofJ'

Related

How to Query three (3) tables having one Identical field in all and Group the result by another field?

I'm performing an analysis on a certain warehouse within a java program using SQL as its database.
I have 3 tables; table1 contains the general details of a products, table2 contains the transaction details of the product in one region i.e. Region A and table 3 contains transaction details of the product in the other region, say Region B.
Table1 - General Details
id itemID itemName Qty category
1 TAG01 Acer Laptops 10 Electronics
2 TAG04 Baking Floor 200 Foods
3 TAG08 Philips Bulbs 700 Electronics
4 TAG109 Sweat Pants 85 Wear
Table2 - details from Region A
id itemID sellDate
1 TAG01 "
2 TAG01 "
3 TAG109 "
4 TAG01 "
Table3 - details from Region B
id itemID sellDate
1 TAG109 "
2 TAG109 "
I want to select category with its Total Qty from Table1 and attach to it the total number of counts of the distinct itemID from Table2 and Table3
Expect results:
id category Qty totalTransactions
1 Electronics 710 3
2 Foods 200sacks -
3 Wear 85 3
This is what I tried and it give satisfying results
String sql = "SELECT category, Qty FROM table1 INNER JOIN (SELECT itemID, COUNT(*) FROM (SELECT itemID FROM table3 union ALL SELECT itemID FROM table2)t GROUP BY itemID)q"
This should give you what you need. I am joining against two sub-queries where each query performs a COUNT per itemId and then the SUM of the two is done in the main query plus the SUM of the Qty column.
LEFT JOIN is used since not all rows in table1 might have rows in the transaction tables.
SELECT category, SUM(Qty), SUM(IFNULL(t3.c3,0) + IFNULL(t2.c2, 0))
FROM table1 t1
LEFT JOIN (SELECT itemId, COUNT(*) c2 FROM table2 GROUP BY itemId) t2 ON t2.itemId = t1.itemID
LEFT JOIN (SELECT itemId, COUNT(*) c3 FROM table3 GROUP BY itemId) t3 ON t3.itemId = t1.itemID
GROUP BY category

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

Using EXISTS in hibernate to return one row only for performance(without setMaxResult)

I have to print a message if ANY duplicate row with same ID (not a primary KEY) exists in table (very large table), that meets a where clause.
Table Person is (There is no Index on table)
PersonPrimaryKEY PersonName ID Email Address InvoiceID TaxID
1 Bob 1 bob#example.com 1/Harton st 1 1
2 John 2 john#example.com 2/Harton st 2 2
3 Peter 1123 peter#example.com 3/Harton st 3 3
I used hibernate
public Collection<Person> readByNameAndID (String name, String ID)
{
TypedQuery<Person> q = getEntityManager ().createNamedQuery("Select p FROM Person p WHERE Name =:Name AND ID <> :ID", Person.class);
q.setParameter ("Name", Name);
q.setParameter ("ID", ID);
return q.getResultList ();
}
Code to use is
if(results.size > 0)
{
System.out.println("Error exists");
}
Problem is, it is very inefficient when reading large table.
How can I make it very efficient ? I was thinking of using EXISTS or COUNT to do that but how to incorporate it with hibernate so that it returns only ONE row then I check size > 0, which will be efficient.
Or is setMaxResult only solution of that ?
Thanks
Aiden
Since you are considering records duplicate on the basis of having the same PersonName, the following HQL query should do the trick:
SELECT COUNT(p)
FROM Person p
GROUP BY p.PersonName
HAVING COUNT(p) > 1
If the count from this query is greater than 0, it means you have duplicates present.

HQL: Counting a child collection conditionally within a query

I'm fairly inexperienced when it comes to HQL, so I was wondering if anyone can help me out on this. I have a relationship where Student has a OneToMany relationship with StudentCollege. I'm trying to write a query that finds each student who has made an application, and how many applications they've submitted. I have the following query written, but I am unsure if it's even close to what I should be doing.
select distinct new ReportVO (stu.id, stu.first_name, stu.last_name, stu.year, stu.school.school_name, sum(case when si.applied = 1 then 1 else 0 end) as numApplications) " +
"from StudentCollege as si join si.student as stu where stu.year <= 12 and stu.user.id = :userId and si.applied = 1 order by stu.last_name
When it runs the following exception is thrown:
org.hibernate.hql.ast.QuerySyntaxException: Unable to locate appropriate constructor on class [package.location.ReportVO] [select distinct new ReportVO (stu.id, stu.first_name, stu.last_name, stu.year, stu.school.school_name, sum(case when si.applied = 1 then 1 else 0 end) as numApplications) from package.location.StudentCollege as si join si.student as stu where stu.year <= 12 and stu.user.id = :userId and si.applied = 1 order by stu.last_name]
However I have a constructor for the VO object that takes in that same number of arguments, so I'm thinking the type returned from the sum is incorrect. I've also tried replacing numApplications with Integer and Count, but the same exception is thrown.
public ReportVO(int id, String firstName, String lastName, int year, String school, Integer numApplications)
Any help is appreciated!
I believe you are a missing a "group by" clause after the where clause.
select distinct new ReportVO (stu.id, stu.first_name, stu.last_name, stu.year, stu.school.school_name, sum(case when si.applied = 1 then 1 else 0 end) as numApplications) " +
"from StudentCollege as si join si.student as stu where stu.year <= 12 and stu.user.id = :userId and si.applied = 1 group by stu.id, stu.first_name, stu.last_name, stu.year, stu.school.school_name order by stu.last_name

Complicated query in mysql database

I have two Tables:
**user**
idU - autoincrement
name
**automobile**
idA - autoincrement
id_user
value
date - type: yyyy-mm-dd
Each user has many automobile
A automobile has only one user
Currently i am using the following query for all users(5 in total):
select sum(value) as user_value, name
from user inner join automobile on user.idU = automobile.id_user
where name like ? AND date = ?
what i want? I want the total value of all user in specific date using 1 query. how can i do this?
#edit1:
TABLE USER
--idU------name----
1 mateus
2 joao
3 maria
TABLE AUTOMOBILE
--idA---id_user----value-------date------
1 1 250 2013-10-13
2 3 322 2013-10-13
3 1 150 2013-10-13
4 3 80 2013-10-13
5 2 100 2013-11-13
6 3 500 2013-11-13
7 1 1100 2013-11-13
8 1 50 2013-12-13
9 3 135 2013-12-13
10 2 40 2013-12-13
I request a query with date = 2013-10-13 and it return:
---name---value
mateus 400
maria 422
Something like:
select sum(value) as user_value,
name inner join automobile on user.idU = automobile.id_user
where date = ?
group by user.idU
-----------EDIT------------
select name, sum(value) as value
from user inner join automobile on user.idU = automobile.id_user
where date = "2013-10-13"
group by automobile.id_user
SQLFiddle here: http://sqlfiddle.com/#!2/8115c/4
Assuming you're wanting a list of each user with the total value of their automobiles with a particular date, you'll need to group by any user fields which you're selecting:
SELECT
SUM(a.value) as user_value,
u.name
FROM
user u INNER JOIN
automobile a on u.idU = a.id_user
WHERE
a.date = ?
GROUP BY
u.name
I am not entirely sure if I got it right. Since you're using aggregate function ('sum') it would normally return only one row containing sum of the values. But if you'd like to execute the query anyway, maybe this would help (not tested)..
UPDATED
SELECT
SUM(automobile.value) as totalValue,
user.name
FROM
automobile
INNER JOIN
user
ON automobile.id_user = user.idu
WHERE
automobile.date between '2013-10-13' and '2013-10-13'
GROUP BY
automobile.id_user

Categories