Query-ing data to get average grade - sql

I am trying to get the average grade by querying data from different tables and while it seems like everything is as it is supposed to be, the query does not work. Tables are the following:
grade(id_grade, id_subject, student_code and grade)
student(student_code, first_name, last_name, birthdate, average_grade)
subject(id_subject, credits, subject_code, name, teacher)
Average grade is calculated using the following formula: (sum of all matching grades (that correspond to student code in student) * average grade from student table + credits from subject * newest added grade) / (sum of all matching grades + credits from the subject).
The query is the following:
UPDATE student AS s
SET
average_grade = (
(SUM(g.grade) * s.average_grade + sub.credits * g.grade) /
(SUM(g.grade) + sub.credits) )
FROM grade AS g, subject AS sub
WHERE s.student_code = g.student_code AND
g.id_subject = sub.id_subject AND s.student_code = 141837;
Keep in mind that this is PostgreSQL so some input may differ from other SQL-s, but this is what I was able to come up with.
I also tried the following as well, but also yielded no results:
UPDATE student AS s
LEFT JOIN grade AS g ON (s.student_code = g.student_code)
LEFT JOIN subject AS sub ON (g.id_subject = sub.id_subject)
SET average_grade = (
(SUM(g.grade) * s.average_grade + sub.credits * g.grade) /
(SUM(g.grade) + sub.credits))
WHERE s.student_code = 141837;
Any help is highly appreciated!

You need to aggregate as a subquery. I don't think the average calculation is correct; I think you want something more like this:
UPDATE student s
SET average_grade = g.average_grade
FROM (SELECT g.student_code,
SUM(g.grade * s.credits) / SUM(s.credits) as average_grade
FROM grade g JOIN
subject s
ON g.id_subject = s.id_subject
GROUP BY g.student_code
) g
WHERE s.student_code = g.student_code AND
s.student_code = 141837;

Related

How can I write a query to do this?

I'm working with this database: link.
I want to write a query to get the student name who belongs to class 2 (class_id = 2) and has the highest average, and the highest degree in a specific course(course_id = 9) at the same time, and if there is no student with these data then return nothing.
I managed to write half of it and get the student with the highest average in class 2, but I didn't know how to add the 2nd condition(highest degree in course 9), I tried in many ways it didn't work.
This is my query: (sorry for the messy code, I'm still learning)
SELECT s.studentname
FROM students s
JOIN section se
ON s.sectionid = se.sectionid
JOIN courses_student cs
ON s.student_id = cs.student_id
WHERE se.classes_id = 2
GROUP BY s.studentname
HAVING ( Avg(cs.exam_season_one)
+ Avg(cs.exam_season_two)
+ Avg(cs.degree_season_one)
+ Avg(cs.degree_season_two) ) / 4 = (SELECT
Max(( Avg(cs.exam_season_one)
+ Avg(cs.exam_season_two)
+ Avg(cs.degree_season_one)
+ Avg(cs.degree_season_two)
) / 4)
FROM students s
JOIN section se
ON s.sectionid =
se.sectionid
JOIN courses_student cs
ON s.student_id =
cs.student_id
WHERE se.classes_id = 2
GROUP BY s.studentname)
The way I calculate the average is:
( Avg(cs.exam_season_one)
+ Avg(cs.exam_season_two)
+ Avg(cs.degree_season_one)
+ Avg(cs.degree_season_two) ) / 4
When the way I calculate the degree in a specific course is:
(cs.exam_season_one
+ cs.exam_season_two
+ cs.degree_season_one
+ cs.degree_season_two ) / 4
So how can I edit my query to add the second condition? or is there a better way than this?
PS: I use oracle
You can find the maximum and the average for each student and then use analytic functions to find the greatest for both conditions:
SELECT studentname
FROM (
SELECT studentname,
ROW_NUMBER() OVER (ORDER BY avg_degree DESC) AS rn_avg,
ROW_NUMBER() OVER (ORDER BY max_degree DESC) AS rn_max
FROM (
SELECT MAX(s.studentname) AS studentname,
AVG(
cs.exam_season_one + cs.exam_season_two + cs.degree_season_one + cs.degree_season_two
) / 4 AS avg_degree,
MAX(
CASE
WHEN cs.courses_id = 9
THEN cs.exam_season_one + cs.exam_season_two + cs.degree_season_one + cs.degree_season_two
END
) / 4 AS max_degree
FROM students s
JOIN section se
ON s.sectionid = se.sectionid
JOIN courses_student cs
ON s.student_id = cs.student_id
WHERE se.classes_id = 2
GROUP BY s.student_id
)
)
WHERE rn_avg = 1
AND rn_max = 1;
Again, don't GROUP BY studentname as you can have two students with the same name.
Which outputs:
STUDENTNAME
maher
sqlfiddle here

How to find highest count of result set using multiple tables in SQL (Oracle)

I have four tables. Here are the skeletons...
ACADEMIC_TBL
academic_id
academic_name
AFFILIATION_TBL
academic_id*
institution_id*
joined_date
leave_date
INSTITUTION_TBL
institution_id
institution_name
REVIEW_TBL
academic_id*
institution_id*
date_posted
review_score
Using these tables I need to find the academic (displaying their name, not ID) with the highest number of reviews and the institution name (not ID) they are currently affiliated with. I imagine this will need to be done using multiple sub-select scripts but I'm having trouble figuring out how to structure it.
this will work:
SELECT at.academic_name,
it.institution_name,
Max(rt.review_score),
from academic_tbl at,
affiliation_tbl afft,
institution_tbl it,
review_tbl rt
WHERE AT.academic_id=afft.academic_id
AND afft.institution_id=it.institution_id
AND afft.academic_id=rt.academic_id
GROUP BY at.academia_name,it.instituton_id
You need an aggregated query that JOINs all 4 tables to count how many reviews were performed by each academic.
Query :
SELECT
inst.institution_name,
aca.academic_name,
COUNT(*)
FROM
academic_tbl aca
INNER JOIN affiliation_tbl aff ON aff.academic_id = aca.academic_id
INNER JOIN institution_tbl inst ON inst.institution_id = aff.institution_id
INNER JOIN review_tbl rev ON rev.academic_id = aca.academic_id AND rev.institution_id = aff.institution_id
GROUP BY
inst.institution_name,
aca.academic_name,
inst.institution_id,
aca.academic_id
NB :
added the academic and institution id to the GROUP BY clause to prevent potential academics or institutions having the same name from being (wrongly) grouped together
if the same academic performed reviews for different institutions, then you will find one row for each academic / institution couple, which, if I understood you right, is what you want
Try this one:
select
inst.institution_name
, aca.academic_name
from
academic_tbl aca
, institution_tbl inst
, affiliation_tbl aff
, review_tbl rev
, (
select
max(rt.review_score) max_score
from
review_tbl rt
, affiliation_tbl aff_inn
where
rt.date_posted >= aff_inn.join_date
and rt.date_posted <= aff_inn.leave_date
and rt.academic_id = aff_inn.academic_id
and rt.institution_id = aff_inn.institution_id
)
agg
where
aca.academic_id = inst.academic_id
and inst.institution_id = aff.institution_id
and aff.institution_id = rev.institution_id
and aff.academic_id = rev.academic_id
and rev.date_posted >= aff.join_date
and rev.date_posted <= aff.leave_date
and rev.review_score = agg.max_score
;
It might return more than one academic, if there are more with the same score (maximum one).

How to use order by when using the group by in LINQ

I have written a LINQ query to display the total number of students supervised by each faculty staff in order of faculty staff's name.
int count;
int key;
var myQuery = from s in Students
join f in Faculties
on s.FID equals f.FID
group f by f.FLast + ' ' + f.FFirst into grp
select new {key = grp.Key, count = grp.Count()};
myQuery.Dump();
This query spits out the result:
Brown Jonnel 1
Zhulin Mark 1
Marx Teresa 3
Langley Colin 1
which is correct. However, there are two things I want to do this result. First I want to order it by the staff name, and second, I want to give those column headers aliases. How do I do this in LINQ?
var myQuery = from s in Students
join f in Faculties
on s.FID equals f.FID
group f by f.FLast + ' ' + f.FFirst into grp
select new { StaffName = grp.Key //give column headers aliases
, count = grp.Count() };
myQuery.ToList().OrderBy(w=>w.StaffName)//order it by the staff name
.Dump();
Hope it help you :-)

Query using subqueries in where statement

I have a query with subqueries in the where statement which works fine but I need to improve on it but I am not sure of the best way to go about this.
Basically I am getting last years results for students in this years classes. The code below works fine for classcode '10DRADRA1' but there are many more classes and I want the query to iterate through all classes for 2017 Semester 1. I need to have the classcode included as one of the output fields also. So the query will start with the first class and give all the results for that class, then do the same for the next class in the subquery. I am not sure how to include the classcode in the select statement next to all the results from the previous year.
Note that students may do a number of classes this year so there may be some repetition of the results from the previous year. Every class they do this year should include the students name and their results from the previous year. I hardcoded '10DRADRA1' in just to get things working, if I remove it it provides all students in the school only once, while I want each class they are in once and the same results for every class they are in.
Also not that vStudentReportsSemesterResults is a view that holds all results, StudentClasses is a table which holds the class codes a student is doing and SubjectClasses is a table holding the codes for all classes in the school
Is anyone able to advise on the best way of doing this? Here is my current code.
SELECT vStudentReportsSemesterResults.StudentID,
vStudentReportsSemesterResults.AssessResultsResult,
vStudentReportsSemesterResults.AssessAreaHdgAbbrev2,
vStudentReportsSemesterResults.FileSemester,
vStudentReportsSemesterResults.FileYear,
vStudentReportsSemesterResults.ClassLearningAreaCode,
vStudentReportsSemesterResults.AssessmentCode,
vStudentReportsSemesterResults.StudentNameInternal
FROM vStudentReportsSemesterResults
WHERE vStudentReportsSemesterResults.StudentID in
(
select StudentClasses.ID from StudentClasses
where StudentClasses.filesemester = 1 and
StudentClasses.fileyear = 2017 and
StudentClasses.classcode in
(
select SubjectClasses.ClassCode from SubjectClasses
where SubjectClasses.FileYear = 2017 and
SubjectClasses.FileSemester = 1 and
SubjectClasses.FileType = 'A' and
SubjectClasses.ClassCampus = 'S' and
SubjectClasses.ClassCode like '10DRADRA1'
)
)
and (vStudentReportsSemesterResults.ClassLearningAreaCode = 'ENG' OR
vStudentReportsSemesterResults.ClassLearningAreaCode = 'MAT' OR
vStudentReportsSemesterResults.ClassLearningAreaCode = 'SCI') AND
(vStudentReportsSemesterResults.AssessAreaHdgAbbrev2 = 'Grade' OR
vStudentReportsSemesterResults.AssessAreaHdgAbbrev2 = 'Level') AND
(vStudentReportsSemesterResults.AssessResultsResult <> '') AND
(vStudentReportsSemesterResults.FileYear = 2016) AND
(vStudentReportsSemesterResults.FileSemester = 4)
Join StudentClasses to vStudentReportsSemesterResults:
SELECT
sc.ClassCode,
srsr.StudentID,
srsr.AssessResultsResult,
srsr.AssessAreaHdgAbbrev2,
srsr.FileSemester,
srsr.FileYear,
srsr.ClassLearningAreaCode,
srsr.AssessmentCode,
srsr.StudentNameInternal
FROM vStudentReportsSemesterResults srsr
JOIN StudentClasses sc
ON sc.ID = srsr.StudentID
AND sc.FileSemester = 1
AND sc.FileYear = srsr.FileYear + 1
AND EXISTS
(
select *
from SubjectClasses subc
where subc.FileYear = sc.FileYear
and subc.ClassCode = sc.ClassCode
and subc.FileSemester = sc.FileSemester
and subc.FileType = 'A'
and subc.ClassCampus = 'S'
)
WHERE srsr.ClassLearningAreaCode in ('ENG', 'MAT', 'SCI')
AND srsr.AssessAreaHdgAbbrev2 in ('Grade', 'Level')
AND srsr.AssessResultsResult <> ''
AND srsr.FileYear = 2016
AND srsr.FileSemester = 4;
This is more readable:
SELECT
ClassCode,
StudentID,
AssessResultsResult,
AssessAreaHdgAbbrev2,
ClassLearningAreaCode,
AssessmentCode,
StudentNameInternal,
lr.FileYear,
lr.FileSemester,
tr.FileYear,
tr.FileSemester,
tr.FileType,
tr.ClassCampus
FROM
vStudentReportsSemesterResults lr
inner join (
select ID, sc.ClassCode, sc.FileYear, sc.filesemester, sbjc.FileType, sbjc.ClassCampus
from StudentClasses sc
INNER JOIN SubjectClasses sbjc ON SC.classcode = sbjc.ClassCode and sc.FileSemester=sbjc.FileSemester and sc.FileYear=sbjc.FileYear
) tr on lr.StudentID = tr.ID
and lr.FileYear = tr.FileYear - 1
WHERE
ClassLearningAreaCode IN ('ENG', 'MAT', 'SCI')
AND AssessAreaHdgAbbrev2 IN ('Grade', 'Level')
AND (AssessResultsResult <> '')
AND lr.FileYear = 2016
AND lr.FileSemester = 4
and tr.FileSemester = 1
and tr.FileType = 'A'
and tr.ClassCampus = 'S'
ORDER BY 2,1,5
In SELECT section I have added some fields to better understand result.
In WHERE clause you can control how to filter comparision.
Put some indexes on join and filter columns and the query will run fast

Get percentages of larger group

The query below is kind of an ugly one so I hope I've got it spaced well enough to make it readable. The query finds the percentage of people that visit a given hospital if they are from a certain area. For instance, if 100 people live in county X and 20 go to hospital A and 80 go to hospital B the query outputs. How the heck is this sort of thing done? Let me know if I need to document the query or whatever I can do to make it clearer.
hospital A 20
hospital B 80
The query below works exactly like I want it to, but it give me thinking: how could this be done for every county in my table?
select hospitalname, round(cast(counts as float)/cast(fayettestrokepop as float)*100,2)as percentSeen
from
(
SELECT tblHospitals.hospitalname, COUNT(tblHospitals.hospitalname) AS counts, tblStateCounties_1.countyName,
(SELECT COUNT(*) AS Expr1
FROM Patient INNER JOIN
tblStateCounties ON Patient.stateCode = tblStateCounties.stateCode AND Patient.countyCode = tblStateCounties.countyCode
WHERE (tblStateCounties.stateCode = '21') AND (tblStateCounties.countyName = 'fayette')) AS fayetteStrokePop
FROM Patient AS Patient_1 INNER JOIN
tblHospitals ON Patient_1.hospitalnpi = tblHospitals.hospitalnpi INNER JOIN
tblStateCounties AS tblStateCounties_1 ON Patient_1.stateCode = tblStateCounties_1.stateCode AND Patient_1.countyCode = tblStateCounties_1.countyCode
WHERE (tblStateCounties_1.stateCode = '21') AND (tblStateCounties_1.countyName = 'fayette')
GROUP BY tblHospitals.hospitalname, tblStateCounties_1.countyName
) as t
order by percentSeen desc
EDIT: sample data
The sample data below is without the outermost query (the as t order by part).
The countsInTheCounty column is the (select count(*)..) part after 'tblStateCounties_1.countyName'
hospitalName hospitalCounts countyName countsInTheCounty
st. james 23 X 300
st. jude 40 X 300
Now with the outer query we would get
st james 0.076 (23/300)
st. jude 0.1333 (40/300)
Here is my guess. You'll have to test against your data or provide proper DDL + sample data.
;WITH totalCounts AS
(
SELECT StateCode, countyCode, COUNT(*) AS totalcount
FROM dbo.Patient GROUP BY StateCode, countyCode
)
SELECT
h.hospitalName,
hospitalCounts = COUNT(p.hospitalnpi),
c.countyName,
countsInTheCounty = tc.totalCount,
percentseen = CONVERT(DECIMAL(5,2), COUNT(p.hospitalnpi)*100.0/tc.totalCount)
FROM
dbo.Patient AS p
INNER JOIN
dbo.tblHospitals AS h
ON p.hospitalnpi = h.hospitalnpi
INNER JOIN
totalCounts AS tc
ON p.StateCode = tc.StateCode
AND p.countyCode = tc.countyCode
INNER JOIN
dbo.tblStateCounties AS c
ON tc.StateCode = c.stateCode
AND tc.countyCode = c.countyCode
GROUP BY
h.hospitalname,
c.countyName,
tc.totalcount
ORDER BY
c.countyName,
percentseen DESC;