JPQL: Switch/case when an object does not exist on the database - sql

I am writing an #Query for a repository method. This query should return all Movie which have an average Rating less than the given parameters value:
#Query(value =
"select m from Movie m " +
"where (select avg(r.rating) from Review r " +
"where m.id = r.movie.id) < :rating")
List<Movie> findMoviesWithAverageRatingLowerThan(#Param("rating") Double rating);
The problem is that I have a requirement that when a Movie has no ratings, it should be interpreted as an average rating equal 0.
I have a clue that I should use switch/case combined with when, but as for now I have no idea how to do it.
Is this even possible to achieve this in one query?

You can try to use COALESCE expression like below:
#Query(value =
"select m from Movie m " +
"where (select avg(COALESCE(r.rating, 0)) from Review r " +
"where m.id = r.movie.id) < :rating")
List<Movie> findMoviesWithAverageRatingLowerThan(#Param("rating") Double rating);

Related

SQL query grammatical exception on converting recursive query to JPQL

This is a JPQl query created from the below mentioned SQL query:
SELECT * FROM NursingSectionHead head where head.secCode IN (" +
"WITH NUR_SECTIONS_SEC_CODE (secCode) " +
"AS (SELECT secCode " +
"FROM NursingSection " +
"WHERE secCode = (SELECT loc.nursingSecCode FROM LocationMast loc WHERE loc.id.locCode = :locCode) " +
"UNION ALL " +
"SELECT C1.secCode " +
"FROM NursingSection C1 " +
"INNER JOIN NUR_SECTIONS_SEC_CODE C2 ON C1.prevSec = C2.secCode)" +
"SELECT * FROM NUR_SECTIONS_SEC_CODE)
This is the SQL query:
select * from IR_TB_NUR_SEC_HEAD head where HEAD.SEC_CODE IN (
WITH NUR_SECTIONS_SEC_CODE (SEC_CODE)
AS (
SELECT SEC_CODE
FROM IR_TB_NUR_SECTIONS
WHERE SEC_CODE = (select LOC.NURSING_SEC_CODE from mst_tb_location_mast loc where LOC.LOC_CODE = 20023)
UNION ALL
SELECT C1.SEC_CODE
FROM IR_TB_NUR_SECTIONS C1
INNER JOIN NUR_SECTIONS_SEC_CODE C2 ON C1.PREV_SEC = C2.SEC_CODE
)
SELECT *
FROM NUR_SECTIONS_SEC_CODE)
When executing it returns the below error:
"org.springframework.dao.InvalidDataAccessResourceUsageException:
could not extract ResultSet; SQL [n/a]; nested exception is
org.hibernate.exception.SQLGrammarException: could not extract
ResultSet"
What can I do about the above exception, please?
The "WITH" clause you are using is known as Common Table Expression (CTE) .I am pretty sure CTE is not supported by JPQL because if it is supported , no one will specially develop a library for supporting CTE query but only for Hibernate.
But you can switch to do use native SQL which should always work provided that your query can really be executed successfully against the database that you are using.

R - How to pass date as a argument in the SQL query

I tried this following query:
studentcal3 <- function(x,y)
{
i<- x
print(i)
j <- y
print(j)
demo1 <-sqldf("SELECT a.StudentName, (b.marks/a.marks) as difference from
(select Date, StudentName, marks from studenthistorydemo WHERE Date= i) as a
INNER JOIN (select Date, StudentName, marks from studenthistorydemo where
Date= j) as b on a.marks= i and b.marks= j WHERE a.marks = i and b.marks =
j and a.StudentName=b.StudentName")
}
And then called the function
studentcal3('2014-01-01','2014-01-02')
Error:- Error in rsqlite_send_query(conn#ptr, statement) : no such column: i
How to resolve this?
P.S - I'm new to R
How should the engine know, which parts are external variables? You must use string concatenation (Read about paste()). And be aware, that you have to pass your variables within single quotes and in a culture and language independant format!
studentcal3 <- function(x,y)
{
i<- x
print(i)
j <- y
print(j)
cmd <-paste("SELECT a.StudentName, (b.marks/a.marks) as difference from ",
"(select Date, StudentName, marks from studenthistorydemo WHERE Date='",i,"') as a ",
"INNER JOIN (select Date, StudentName, marks from studenthistorydemo where ",
"Date='",j,"') as b on a.marks='",i,"' and b.marks='",j,
"' WHERE a.marks ='",i,"' and b.marks ='",j,"' and a.StudentName=b.StudentName",sep="")
#demo1 <-sqldf(cmd)
print(cmd)
}
studentcal3('2014-01-01','2014-01-02')
The result
"SELECT a.StudentName, (b.marks/a.marks) as difference from (select
Date, StudentName, marks from studenthistorydemo WHERE
Date='2014-01-01') as a INNER JOIN (select Date, StudentName, marks
from studenthistorydemo where Date='2014-01-02') as b on
a.marks='2014-01-01' and b.marks='2014-01-02' WHERE a.marks
='2014-01-01' and b.marks ='2014-01-02' and a.StudentName=b.StudentName"
And be aware of sql injection! You might build a statment with real parameters to avoid injection attacks.
If this statement really delivers what you expect is another question. You might print it out and paste it into a query window to test it first...

Sql Joins on multiple table returning product of two columns

I am trying to generate a report on a sql server database in asp.net and I am getting the results of some columns as a product of two columns. Here is the code
comm.CommandText = "SELECT Count(Courses.CourseID) AS CourseCount, Count(Students.StudentID) AS StudentCount, Schools.Name, Schools.StartDate, Schools.SchoolFees " +
"FROM Schools" +
"LEFT JOIN Courses ON (Schools.SchoolID = Courses.SchoolID)" +
"LEFT JOIN Students ON (Schools.SchoolID = Students.SchoolID) " +
"WHERE Schools.Active = 1 " +
"GROUP BY Schools.Name, Schools.StartDate, Schools.SchoolFees";
When I run the code, the result displays, but the columns for "CourseCount" and "StudentCount" display a value that is a product of each individual column. "CourseCount" is normally 288 and "StudentCount" is 38 but when I run the code, both "CourseCount" and "StudentCount" display 10944 which is 38 x 288.
Anyway I can make them display the correct values?
Changing your code from using a count of all rows, to a count of distinct values only, should return the results you expect
comm.CommandText = "SELECT Count(DISTINCT Courses.CourseID) AS CourseCount, Count(DISTINCT Students.StudentID) AS StudentCount, Schools.Name, Schools.StartDate, Schools.SchoolFees " +
"FROM Schools" +
"LEFT JOIN Courses ON (Schools.SchoolID = Courses.SchoolID)" +
"LEFT JOIN Students ON (Schools.SchoolID = Students.SchoolID) " +
"WHERE Schools.Active = 1 " +
"GROUP BY Schools.Name, Schools.StartDate, Schools.SchoolFees";
The results being returned are technically correct, if all schools have courses, and all courses have students
As stated above, it is how you are using the COUNT (), You are asking it to count all, which is why it returns so many. Use count on just the two values you want counted.
This might perform better than the DISTINCT Count method..
comm.CommandText =
"SELECT cc.CourseCount, sc.StudentCount, Schools.Name, Schools.StartDate, Schools.SchoolFees " +
"FROM Schools" +
"OUTER APPLY (SELECT Count(Students.StudentID) StudentCount FROM Students WHERE Students.SchoolID = Schools.SchoolID) sc " +
"OUTER APPLY (SELECT Count(Courses.CourseID) CourseCount FROM Courses WHERE Courses.SchoolID = Schools.SchoolID) cc " +
"WHERE Schools.Active = 1 ";

How to use a group by Sum SQL with Spring Data JPA?

I want to load best seller products by quantity. These are my tables:
Product
id name
1 AA
2 BB
Productorder
order_id product_id quantity
1 1 10
2 1 100
3 2 15
4 1 15
This is my Spring Data Repository:
#Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
#Query(value = "select top 5 p.name, sum(po.quantity) as total_quantity from product p " +
"inner join productorder po " +
"on p.id = po.product_id " +
"group by p.id, p.name " +
"order by total_quantity desc", nativeQuery = true)
List<Product> findTopFiveBestSeller();
}
I am getting HsqlException: Column not found: id
I think this error does not have anything to do with id column, as it is there for both tables. Do "group by Sum queries" work with Spring data? Because it seems little strange for me as Spring Data should select just product attributes from the database, and with this sql we are selecting also the sum(po.quantity). Can Spring data handle this and convert the result as a List?
PS: I am using HSQLDB embedded as DB.
After changing the select statements projection from p.name to p.* to indicate that I am selecting multiple values rather than just String objects that have to be magically converted to Product objects, this works:
#Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
#Query(value = "select top 5 p.*, sum(po.quantity) as total_quantity from product p " +
"inner join productorder po " +
"on p.id = po.product_id " +
"group by p.id, p.name " +
"order by total_quantity desc", nativeQuery = true)
List<Product> findTopFiveBestSeller();
}
Thanks #JMK und #JB Nizet.

SQL on how to simultaneously compare two column values in a query

I need create a query to find the names of drivers whose exam scores get lower when they take more exams. So I have the following tables:
branch(branch_id, branch_name, branch_addr, branch_city, branch_phone);
driver(driver_ssn, driver_name, driver_addr, driver_city, driver_birthdate, driver_phone);
license(license_no, driver_ssn, license_type, license_class, license_expiry, issue_date, branch_id);
exam(driver_ssn, branch_id, exam_date, exam_type, exam_score);
**the exam_date is a date
So I am using the tables driver and exam. I would like to somehow check that the exam_date>anotherDate while at the same time checking that exam_score
*EDIT
This is what I came up with but I feel like some of the syntax is illegal. I keep getting a syntax error.
s.executeQuery("SELECT driver_name " +
"FROM driver " +
"WHERE driver.driver_ssn IN " +
"(SELECT e1.driver_ssn" +
"FROM exam e1" +
"WHERE e1.exam_score < " +
"(SELECT e2.exam_score FROM exam e2)" +
"AND e1.exam_date > " +
"(SELECT e2.exam_date FROM exam e2)");
EDIT! I got it working! Thanks for your input everyone!
SELECT driver.driver_name
FROM driver
WHERE driver.driver_ssn IN
(SELECT e1.driver_ssn
FROM exam e1, exam e2, driver d
WHERE e1.exam_score < e2.exam_score
AND e1.exam_date > e2.exam_date
AND e1.driver_ssn=e2.driver_ssn)
You will need to do a self join. See this example and work it to your schema.
select d.name,
es.date_taken as 'prev date',
es.score as 'prev score',
es.date_taken as 'new date',
es_newer.score as 'new score'
from driver d
inner join exam_score es
on d.id = es.driver_id
left outer join exam_score es_newer
on d.id = es_newer.driver_id
and es_newer.date_taken > es.date_taken
and es_newer.score < es.score
where es_newer.id is not null
Here is a SQL Fiddle that I made to demonstrate.
SELECT returns a set and you cannot compare a single value against a set. You can try something along these lines. This is similar to yours and doesn't handle three exam case :-
SELECT driver_name
FROM driver
JOIN exam e1 ON driver_ssn
JOIN exam e2 ON driver_ssn
WHERE e1.exam_score < e2.exam_score
AND e1.exam_date > e2.exam_date
The query selects all pairs of exams taken by a driver in which the score is less and the date big
Simple take on this problem would be getting drivers who take a couple of exams and their second score is lower.
To compare columns from the same table SQL uses self-join. Your join condition should include:
select e1.driver_ssn, e1.exam_type, e1.exam_score as score_before,
e2.exam_score as score_after
exam e1 join exam e2 on (e1.driver_ssn = e2.driver_ssn and
e1.exam_type = e2.exam_type and
e1.exam_date < e2.exam_date and
e1.exam_score > e2.exam_score)