Using SQL to calculate GPA , getting "Invalid Identifier" error - sql

I'm trying to calculate the GPA of each student in between 4 tables:
Student(STUDENT_ID, STUDENT_LNAME, STUDENT_FNAME, MAJOR)
Course(COURSE_NO, COURSE_NAME, DEPT_CODE, CREDITS)
Grade(COURSE_NO, STUDENT_ID, GRADE)
Grade-point(GRADE, POINTS)
This is the code I've written:
SELECT S.STUDENT_ID, S.STUDENT_LNAME, S.STUDENT_FNAME,
SUM(C.CREDITS*P.POINTS)/SUM(C.CREDITS) AS GPA
FROM GRADE_POINT P, STUDENT S, COURSE C, GRADE G
WHERE S.STUDENT_ID=G.STUDENT_ID
AND C.COURSE_NO=G.COURSE_NO
AND G.GRADE=P.GRADE
GROUP BY S.STUDENT_ID, S.STUDENT_LNAME, S.STUDENT_FNAME, GPA;
I haven't been taught yet the CREATE PROCEDURE query, therefore I'm not supposed to use it at this level. However, the code doesn't work. It says GPA is an invalid identifier. I honestly can't tell how it's wrong. I also tried removing as many functions as possible for the variable, like this:
SELECT S.STUDENT_ID, S.STUDENT_LNAME, S.STUDENT_FNAME, (C.CREDITS*P.POINTS) AS
TOT_CR_PT, SUM(C.CREDITS) AS TOT_CREDIT, (TOT_CR_PT/TOT_CREDIT) AS GPA
But the problem still exists with GPA. What's the problem and how can I fix this?

You don't want to be grouping by GPA, as this is the alias you've given to the SUM() Aggregate that you've applied to the set that you have grouped on (Student). Just remove it from the GROUP BY:
SELECT S.STUDENT_ID, S.STUDENT_LNAME, S.STUDENT_FNAME,
SUM(C.CREDITS*P.POINTS)/SUM(C.CREDITS) AS GPA
FROM GRADE_POINT P, STUDENT S, COURSE C, GRADE G
WHERE S.STUDENT_ID=G.STUDENT_ID
AND C.COURSE_NO=G.COURSE_NO
AND G.GRADE=P.GRADE
GROUP BY S.STUDENT_ID, S.STUDENT_LNAME, S.STUDENT_FNAME;
One other point which might get you marks is to use JOIN instead of (ab)using the WHERE clause (recent versions of Oracle do support this syntax):
FROM STUDENT S
INNER JOIN GRADE G
ON S.STUDENT_ID=G.STUDENT_ID
...

Related

How to get MAX value out of the GROUPs COUNT

I've recently started to learn tsql beyond basic inserts and selects, I have test database that I train on, and there is one query that I can't really get to work.
There are 3 tables used in that query, in the picture there are simplified fields and relations
I have 2 following queries - first one is simply displaying students and number of marks from each subject. Second is doing almost what I want to achive - shows students and maxiumum amount of marks they got, so ex.
subject1 - (marks) 1, 5, 3, 4 count - 4
subject2 - (marks) 5, 4, 5 - count - 3
Query shows 4 and from what I checked it returns correct results, but I want one more thing - just to show the name of the subject from which there is maximum amount of marks so in the example case - subject1
--Query 1--
SELECT s.Surname, subj.SubjectName, COUNT(m.Mark) as Marks_count
FROM marks m, students s, subjects subj
WHERE m.StudentId = s.StudentNumber and subj.SubjectNumber = m.SubjectId
GROUP BY s.Surname, subj.SubjectName
ORDER BY s.Surname
--Query 2--
SELECT query.Surname, MAX(Marks_count) as Maximum_marks_count FROM (SELECT s.Surname, subj.SubjectNumber, COUNT(m.Mark) as Marks_count
FROM marks m, students s, subjects subj
WHERE marks.StudentId = s.StudentNumber and subj.SubjectNumber = m.SubjectId
GROUP BY s.Surname, subj.SubjectName) as query
GROUP BY query.Surname
ORDER BY query.Surname
--Query 3 - not working as supposed--
SELECT query.Surname, query.SubjectName, MAX(Marks_count) as Maximum_marks_count FROM (SELECT s.Surname, subj.SubjectNumber, COUNT(m.Mark) as Marks_count
FROM marks m, students s, subjects subj
WHERE marks.StudentId = s.StudentNumber and subj.SubjectNumber = m.SubjectId
GROUP BY s.Surname, subj.SubjectName) as query
GROUP BY query.Surname, query.SubjectName
ORDER BY query.Surname
Part of the query 1 result
Part of the query 2 and unfortunately query 3 result
The problem is that when I add to the select statement subject name I got results as from query one - there is no more maximum amount of marks just students, subjects and amount of marks from each subject.
If someone could say what I'm missing, I will much appreciate :)
Here's a query that gets the highest mark per student, put it at the top of your sql file/batch and it will make another "table" you can join to your other tables to get the student name and the subject name:
WITH studentBest as
SELECT * FROM(
SELECT *, ROW_NUMBER() OVER(PARTITION BY studentid ORDER BY mark DESC) rown
FROM marks) a
WHERE rown = 1)
You use it like this (for example)
--the WITH bit goes above this line
SELECT *
FROM
studentBest sb
INNER JOIN
subject s
ON sb.subjectid = s.subjectnumber
Etc
That's also how you should be doing your joins
How does it work? Well.. it establishes an incrementing counter that restarts every time studentid changes (the partition clause) and the numberin goes in des ending mark order (the order by clause). An outer query selects only those rows with 1 in the row number, ie the top mark per student
Why can't I use group by?
You can, but you have to write a query that summarises the marks table into the top mark (max) per student and then you have to join that data back to the mark table to retrieve the subject and all in it's a lot more faff, often less efficient
What if there are two subjects with the same mark?
Use RANK instead of ROW_NUMBER if you want to see both
Edit in response to your comment:
An extension of the above method:
SELECT * FROM
(
SELECT *, ROW_NUMBER() OVER(PARTITION BY su, st ORDER BY c DESC) rn FROM
(
SELECT studentid st, subjectid su, count(*) c
FROM marks
GROUP BY st, su
) a
) b
INNER JOIN student stu on b.st = stu.studentnumber
INNER JOIN subject sub on b.su = sub.subjectnumber
WHERE
b.rn = 1
We count the marks by student/subject, then rownumber them in descending order of count per student-subject pair, then choose only the first row and join in the other wanted data
Ok thanks to Caius Jard, some other Stack's question and a little bit of experiments I managed to write working query, so this is how I did it.
First I created view from query1 and added one more column to it - studentId.
Then I wrote query which almost satisfied me. That question helped me a lot with that task: Question
SELECT marks.Surname,
marks.SubjectName,
marks.Marks_count,
ROW_NUMBER() OVER(PARTITION BY marks.Surname ORDER BY marks.Surname) as RowNum
FROM MarksAmountPerStudentAndSubject marks
INNER JOIN (SELECT MarksAmountPerStudentAndSubject.Id,
MAX(MarksAmountPerStudentAndSubject.Marks_count) as MaxAmount
FROM MarksAmountPerStudentAndSubject
GROUP BY MarksAmountPerStudentAndSubject.Id) m
ON m.Id = marks.Id and marks.Marks_count = m.MaxAmount
It gives following results
That's what I wanted to achieve with one exception - if students have the same amount of marks from multiple subjects it displays all of them - thats fine but I decided to restrict this to the first result for each student - I couldn't just simply put TOP(1)
there so I used similar solution that Caius Jard showed - ROW_NUMBER and window function - it gave me a chance to choose records that has row number equals to 1.
I created another view from this query and I could simply write the final one
SELECT marks.Surname, marks.SubjectName, marks.Marks_count
FROM StudentsMaxMarksAmount marks
WHERE marks.RowNum = 1
ORDER BY marks.Surname
With result

SQL Select Max within a Subquery

I've been having trouble with a SQL subquery, and although I imagine this is fairly basic, the internet does not seem to hold the answer. I have a subquery inside a FROM statement which has a MAX() function within it, and I cannot seem to reference this data in the rest of the query. The query is here:
SELECT
m.nameFirst, m.nameLast, t.salary, te.name
FROM
(SELECT
MAX(salary), teamID
FROM
salaries
GROUP BY
teamID) AS t, master AS m, teams AS te, salaries AS s
WHERE
t.salary = s.salary
AND s.teamID = t.teamID
AND s.playerID = m.playerID
AND te.teamID = t.teamID;
The subquery, when run by itself, returns results which look like this:
+-------------+--------+
| MAX(salary) | teamID |
+-------------+--------+
| 13166667 | ANA |
| 16000000 | ARI |...
However, when I try to run the whole query, I get the following result:
ERROR 1054 (42S22): Unknown column 't.salary' in 'field list'
I have tried a few different things, such as t.MAX(salary), MAX(t.salary), and even just t.*, but as I need to use the subquery's results later, it just throws different errors.
What name should I use to call the results of the MAX column of the subquery?
Thanks so much for any help.
you can use an alias for max column and your code will work :
SELECT m.nameFirst, m.nameLast, t.salary, te.name
FROM (
SELECT MAX(salary) as salary, teamID
FROM salaries
GROUP BY teamID
) AS t, master AS m, teams AS te, salaries AS s
WHERE t.salary=s.salary AND s.teamID=t.teamID AND s.playerID = m.playerID AND te.teamID=t.teamID;
you can give it an alias to make it easier or in this case accessible
eg
SELECT MAX(salary) as max_salary, teamID ...
then later simply reference
t.max_salary
so in your example change it like this
SELECT m.nameFirst, m.nameLast, t.max_salary, te.name
FROM (
SELECT MAX(salary) as max_salary, teamID
-- rest of query
This is because there is no salary returned in subquery likewise the error name it as max(salary) as some_name then t.some_name=s.salary

SQL,Nested Queries (MS ACCESS)

I am trying to tackle a problem but seem to be getting nowhere. I want to display Grade 12 students who scored below average for Maths then instead of displaying their average display their maths marks instead.
I am using msAccess and suspect the use of nested queries are necessary.The fields I am working with are first_name, last_name, grade (from 1 to 12) and Maths (containing maths marks)
I have this:
Select first_name,last_name,maths
FROM students
WHERE grade = 12
HAVING ROUND(AVG(maths),1)< maths;
Output:
Error:
You tried to execute a query that does not include the specified expression 'first_name' as part of an aggregate function
However, I do not know why it is throwing this error and it repeats like this even after removing the field from select which I don't want to do in the first place because I need to display it
To get the users who scored below the average, you can do a query similar to yours, but with a group by:
select s.student_id, avg(maths) as avg_maths
from students as s
where s.grade = 12
group by s.student_id
having avg(maths) < (select avg(maths) from students where grade = 12);
(Note: This assumes that you have an id for each student, rather than using the name.)
Next, you can get the original maths scores in various ways. One simple way uses in:
select first_name, last_name, maths
from students
where grade = 12 and
student_id in (select s.student_id, avg(s.maths) as avg_maths
from students as s
where s.grade = 12
group by s.student_id
having avg(maths) < (select avg(maths) from students where grade = 12)
);

How to fetch max grade for same course in different academic session

I am working on PEOPLESOFT CMS . Students enroll in courses and
receive grades. Sometimes a student repeats a course and gets a
better grade. I need to calculate the GPA by using only the best
grade. So for each student that repeats courses, I have to determine
what the highest grade is.E.g. in 2nd semester he got 3 grade_point
but in in 4 semester he improved to 4.5 grad_point then query should
fetch 4.5 instead of 3 when we notify result for all 4 semester but
when we notify for upto 2nd semester its could return 3
grad_point.here i construct a sql which working fine in selection bt
when i make it view then i got a problem for handling it different
semester its always return max for 4th semester result bt in view
its missed the course upto 2 semester gpa here is query
SELECT DISTINCT A.institution
,A.acad_career
,A.emplid
,a.crse_id
,A.UNT_TAKEN
, a.acad_prog
,first_value(a.grade_points) OVER (PARTITION BY A.emplid
,a.crse_id
ORDER BY a.grade_points DESC) AS GPA
,first_value(a.strm) OVER (PARTITION BY A.emplid
,a.crse_id
ORDER BY a.grade_points DESC) AS strm
FROM ps_qau_maxgp_ugrd a
WHERE acad_career='UGRD'
AND Emplid LIKE '04091313014%'
AND Strm='1313'
ORDER BY A.institution ,A.acad_career ,A.emplid ,A.UNT_TAKEN , a.acad_prog
I believe you need to look at the date the grade was entered. For a given term and class-nbr, you should pull the greatest grade:
pseudo code:
select
max(date_entered)
group by
institution,
acad_career,
crse_id,
term,
class_nbr(if you have it)
The problem is that with the table you are using, i don't beleive is the actual enrollment table. I am only familiar with CS, so i would be looking at the ps_stdnt_enrl table or the ps_stdnt_car_term to get the accumulated data for a term.
Just use MAX and GROUP BY.
SELECT A.institution,
A.acad_career,
A.emplid,
a.crse_id,
A.unt_taken,
a.acad_prog,
Max(a.grade_points) GPA,
Max(a.strm) AS strm
FROM ps_qau_maxgp_ugrd a
WHERE acad_career = 'UGRD'
AND emplid LIKE '04091313014%'
AND strm = '1313'
GROUP BY A.institution,
A.acad_career,
A.emplid,
a.crse_id,
A.unt_taken,
a.acad_prog;

Two Table problems with oracle

Students
ID FName Lname Status Major Code GPA Admitted Date
104 Donald Nento Sophomore 105 2.64 1-Jul-2015
Departments
Dept Code Dept Name College
105 Mathematics AS
These are the above tables... I am stuck on two questions:
List the college of the student with the highest GPA.
List Number of days elapsed since admission for each student.
Can anyone shed some light please?
List the college of the student with the highest GPA.
You can get all the college(s) with the maximum GPA without using a correlated sub-query like this:
SELECT College
FROM (
SELECT College,
RANK() OVER ( ORDER BY GPA DESC ) AS gpa_rank
FROM Students s
INNER JOIN
Departments d
ON ( s."Major Code" = d."Dept Code" )
)
WHERE gpa_rank = 1;
List Number of days elapsed since admission for each student.
SELECT ID,
FNAME,
LNAME,
FLOOR( SYSDATE - "Admitted Date" ) AS days_since_admission
FROM students;
You could use TRUNC(SYSDATE) - "Admitted Date" but if the admitted date has a time component then it will not be a round number.
(Note: it is unclear what your column names actually are. Your data shows them as case sensitive and with spaces but this is unusual as it is more usual to have case insensitive column names with underscores instead of spaces. I've used "" to match the column names used in your post but please adjust the names to whatever the actual values are.)
Here are some suggested queries. You'll need to adapt field names if they don't match. If you use double quotes the names are case sensitive, but if you have spaces in them, then you need to use double quotes:
List the college of the student with the highest GPA.
SELECT d."Dept Name"
FROM Departments d
INNER JOIN Students s
ON d."Dept Code" = s."Major Code"
WHERE s.GPA = (SELECT MAX(GPA) FROM Students);
Or, if you are not allowed to use INNER JOIN then:
SELECT d."Dept Name"
FROM Departments d,
Students s
WHERE d."Dept Code" = s."Major Code"
AND s.GPA = (SELECT MAX(GPA) FROM Students);
List Number of days elapsed since admission for each student.
SELECT s.*,
TRUNC(SYSDATE) - s.Admitted_Date AS days_since_admission
FROM Students s