Sql Joins on multiple table returning product of two columns - sql

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 ";

Related

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

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);

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.

PostgreSQL joining multiple tables

Hi guys I am trying to join tables here, I want to add table called Table2 so that students from that table are displayed too. The thing which is confusing is I already have joins in this query, so how should add another join so that students from that Table2 are displayed as well, without breaking the existing code . Also, there is no way I can check output
The ID varables are stsidno in stTable 1, sidno in Table2, scsidno in table schedprd
Here's the code:
String selS = "select distinct stdistrict,stschool,stsidno,"
+ " sname as name,stgrade,"
+ "S.recnum as recnum, S.stldate as stldate,scsec,sctea,sccor,scgrade,scclsprd,scgrdprd,"
+ "case when P.scchangestartdate is null then C.clstart else "
+ "P.scchangestartdate end as scchangestartdate, "
+ "case when S.stedate is null or S.stedate<C.clstart "
+ "then C.clstart else S.stedate end as stedate "
+ "from stTable1 as S join schedprd as P on "
+ "(scyear=styear and scdistrict=stdistrict and scschool=stschool "
+ "and stsidno=scsidno and (scsec is not null and scsec<>'')) "
+ "left outer join calendar as C on (C.clyear=S.styear and "
+ "C.cldistrict=S.stdistrict and C.clschool=S.stschool and C.cltype='10') "
+ "where styear=? and stdistrict=? ";
You could UNION Table1 and Table2 together in a Common Table Expression (CTE) and then reference the CTE instead of Table1 in your query, like below:
WITH CTE
AS
(
SELECT *
FROM Table1
UNION
SELECT *
FROM Table2
)
select distinct
stdistrict,
stschool,
stsidno,
sname as name,
stgrade,
S.recnum as recnum,
S.stldate as stldate,
scsec,
sctea,
sccor,
scgrade,
scclsprd,
scgrdprd,
case when P.scchangestartdate is null then C.clstart else P.scchangestartdate end as scchangestartdate,
case when S.stedate is null or S.stedate<C.clstart then C.clstart else S.stedate end as stedate
from CTE as S
join schedprd as P on (scyear=styear and scdistrict=stdistrict and scschool=stschool and stsidno=scsidno and (scsec is not null and scsec<>''))
left outer join calendar as C on (C.clyear=S.styear and C.cldistrict=S.stdistrict and C.clschool=S.stschool and C.cltype='10')
where styear=? and stdistrict=?;

Access Select Query to show the same field with a different criteria

I have a select query in Access 2010 which shows a 'Level Name' where the 'Period ID' is set to 1
How can i add another field to the query which also shows the Level Name where the Period ID is set to 2?
I would like them both to be next to each other in separate rows
For reference, my current query looks like:
SELECT
qryMaster.[First Name] & " " & qryMaster.[Last Name] AS ChildName,
qryMaster.LevelName
FROM
qryMaster
WHERE
(
(qryMaster.ClassID = [Forms]![frmViewRecords_subject]![cboClass])
AND
(qryMaster.SubjectID = [Forms]![frmViewRecords_subject]![cboSubject])
AND
(qryMaster.PeriodID = 1)
);
I have added a picture of how my current query looks like in a sub form. I would like a new Column next to september, but for a different period
A WHERE clause is always for all fields. So if you want two different WHERE clauses then you need two queries.
Otherwise try
AND (qryMaster.PeriodID In(1,2))
I know this thread was active five month ago, but I had the same problem, found this page with google. Asuming others will arrive here too, I'll post the two solutions that I found below.
SELECT t1.ChildName, t1.LevelName1, qryMaster.LevelName
FROM
((SELECT
qryMaster.[First Name] & " " & qryMaster.[Last Name] AS ChildName,
qryMaster.LevelName as LevelName1
FROM qryMaster
WHERE
(
(qryMaster.ClassID = [Forms]![frmViewRecords_subject]![cboClass])
AND
(qryMaster.SubjectID = [Forms]![frmViewRecords_subject]![cboSubject])
AND
(qryMaster.PeriodID = 1)
)) AS t1)
LEFT JOIN qryMaster
ON t1.ChildName = (qryMaster.[First Name] & " " & qryMaster.[Last Name])
WHERE (
(qryMaster.ClassID = [Forms]![frmViewRecords_subject]![cboClass])
AND
(qryMaster.SubjectID = [Forms]![frmViewRecords_subject]![cboSubject])
AND
(qryMaster.PeriodID = 2)
)
As I do not have your data I couldn't test the code for your data, therefore I am not sure whether the "ON" clause works like this. In my case I have some customer ID there.
And the second one:
SELECT t1.[First Name] & " " & t1.[Last Name] AS ChildName, t1.LevelName AS LevelName1, t2.levelName AS LevelName2
FROM qryMaster t1
INNER JOIN qryMaster t2 ON t1.Childname=(t2.[First Name] & " " & t2.[Last Name])
WHERE (
(t1.ClassID = [Forms]![frmViewRecords_subject]![cboClass]) AND
(t1.SubjectID = [Forms]![frmViewRecords_subject]![cboSubject]) AND
(t1.PeriodID = 1) AND
(t2.ClassID = [Forms]![frmViewRecords_subject]![cboClass]) AND
(t2.SubjectID = [Forms]![frmViewRecords_subject]![cboSubject]) AND
(t2.PeriodID = 2)
)

SQL returning the count from wrong table

This SQL is returning the record count of table Dims instead returning the record count of table BIDdetails. How can I fix?
BIDReportSearch.CommandText = ("SELECT BIDdetails.Origin, BIDdetails.Destination,
Round(Sum(Dims.ChargeableWeight)) as CWeight, count(BIDdetails.Origin) as NoOfShpt
FROM BIDdetails LEFT JOIN DIMS ON BidDetails.BID=Dims.BID
where BIDdetails.OrgCountry<>'AE' and BIDdetails.DestCountry='AE' and
BIDdetails.ClosingDate>=#" & dtpBIDfrom.Value & "# and BIDdetails.ClosingDate<=#" &
dtpBIDto.Value & "# GROUP BY BIDdetails.Origin, BIDdetails.Destination
ORDER BY Round(Sum(Dims.ChargeableWeight)) DESC")
The expression:
count(BIDdetails.Origin)
simply counts the number of non-NULL values of BIDdetails.Origin in each group. Because you are actually grouping by the field, that would be the number of rows in each group.
You can get what you want in most databases by using count(distinct) on a unique identifier. Alas, MS Access doesn't support count(distinct) so such a query is much harder to write in Access. You can get just the count field by doing:
SELECT BIDdetails.Origin, BIDdetails.Destination, count(*) as NoOfShpt
FROM BIDdetails
where BIDdetails.OrgCountry <> 'AE' and BIDdetails.DestCountry='AE' and
BIDdetails.ClosingDate>=#" & dtpBIDfrom.Value & "# and BIDdetails.ClosingDate<=#" & dtpBIDto.Value & "#
GROUP BY BIDdetails.Origin, BIDdetails.Destination;
And then combining the results either in your application or by joining this query back into the original one.
EDIT:
This is your original query:
SELECT d.Origin, d.Destination,
Round(Sum(Dims.ChargeableWeight)) as CWeight, count(d.Origin) as NoOfShpt
FROM BIDdetails as d LEFT JOIN
DIMS
ON BidDetails.BID=Dims.BID
where d.OrgCountry <> 'AE' and d.DestCountry='AE' and
d.ClosingDate> = #" & d.Value & "# and d.ClosingDate<=#" & dtpBIDto.Value & "#
GROUP BY d.Origin, d.Destination
ORDER BY Round(Sum(Dims.ChargeableWeight)) DESC
There is another approach, where you aggregate first by the details and then again. I think that is easier in this case:
SELECT Origin, Destination, SUM(CWeight) as CWeight, COUNT(*) as NumShip
FROM (SELECT d.id, d.Origin, d.Destination,
Round(Sum(Dims.ChargeableWeight)) as CWeight, count(d.Origin) as NoOfShpt
FROM BIDdetails as d LEFT JOIN
DIMS
ON BidDetails.BID = Dims.BID
where d.OrgCountry <> 'AE' and d.DestCountry='AE' and
d.ClosingDate> = #" & d.Value & "# and d.ClosingDate<=#" & dtpBIDto.Value & "#
GROUP BY d.id, d.Origin, d.Destination
) as d
GROUP BY Origin, Destination
ORDER BY Round(Sum(CWeight)) DESC;
d.id refers to whatever the unique id is for what you want to count.