Finding class average and number of student per subject - sql-server

How can I get column for class average per subject and column for No. of students offering each subject? I have created the following tables statement and queries. Below is my query that gives me the result of a student without class average and No. of students per subject.
Table Student has three columns containing three students
Studentid Firstname Lastname
--------------------------------
1 Oreofeoluwa Ogunkoya
2 Prevailer Adebayo
3 Arike Adeladan
4 Khalilat Yakubu
Table course contains four courses Irk and Crk are optional.
Courseid Course
------------------
1 Maths
2 English
3 Irk
4 Crk
I also have StudentCourse as a junction table for course and student. It contains score for all students.
SELECT Course
,Score
,Grade
,Comment
,Pos
,Minimum
,Maximum
FROM (
SELECT S.firstname
,S.lastname
,C.course
,Sc.score
,CASE
WHEN Score BETWEEN 80
AND 100
THEN 'A'
WHEN Score BETWEEN 70
AND 79
THEN 'B'
WHEN Score BETWEEN 60
AND 69
THEN 'C1'
WHEN Score BETWEEN 50
AND 59
THEN 'C2'
WHEN Score BETWEEN 40
AND 49
THEN 'D'
ELSE 'F'
END AS Grade
,CASE
WHEN Score BETWEEN 80
AND 100
THEN 'Excellent'
WHEN Score BETWEEN 70
AND 79
THEN 'Very Good'
WHEN Score BETWEEN 60
AND 69
THEN 'Good'
WHEN Score BETWEEN 50
AND 59
THEN 'Average'
WHEN Score BETWEEN 40
AND 49
THEN 'Pass'
ELSE 'Fail'
END AS Comment
,Rank() OVER (
PARTITION BY course ORDER BY Score DESC
) AS Pos
,Min(Score) OVER (
ORDER BY course
) AS Minimum
,Max(Score) OVER (
ORDER BY course
) AS Maximum
FROM Student S
JOIN Studentcourse Sc ON S.Studentid = Sc.Studentid
JOIN Courses C ON C.courseid = Sc.Courseid
) sub
WHERE firstname = 'Oreofeoluwa'
This query gives me the following table but i need the class average and no of students offering each subject
Course Score Grade Comment Pos Minimum Maximum
---------------------------------------------------
Crk 62.00 C1 Good 1 44.00 62.00
English 80.00 A Excellent 1 43.00 80.00
Maths 96.00 A Excellent 1 36.00 96.00

You have to group it at the course level to get the average scores at a course level. Please find below a sample query:
Select course,avg(score) as avg_score,count(distinct studentid) as students
from Student S
JOIN Studentcourse Sc ON S.Studentid = Sc.Studentid
JOIN Courses C ON C.courseid = Sc.Courseid
Group by course
If you want this added to the table you have created at a student level, then you will have to perform a left join. Hope this helps.

Related

SQL: insert total count row after joining table

The first step is to join staff and customer together. The second step is to count the distinct product_id. My target is to add the total(sum) field under the result table.
Thanks.
staff
staff_ID Name cust_id
1 Tom 101
1 Tom 101
1 Tom 105
2 Peter 102
2 Peter 104
3 Billy 103
customer
cust_id product_id
101 A1
102 A2
103 A3
104 A4
105 A5
My work:
SELECT a.staff_name,COUNT(DISTINCT a.product_id)
FROM (SELECT distinct a.staff_id, a.staff_name, a.cust_id
FROM staff)a
LEFT JOIN customer b ON a.cust_id=b.cust_id
GROUPBY a.staff_name
What I want is to add the total column below the count.
Name count
Tom 2
Peter 2
Billy 1
Total 5
Update:
Regarding the "Total", as #MatBailie correctly pointed out in the comments:
The aggregate of multiple COUNT(DISTINCT) rows CAN NOT be guaranteed to be summable. If two staff members share the same product_id the summary value will be LESS THAN the sum of its members.
So for this sample data set:
db<>fiddle here
cust_id
product_id
101
A1
102
A2
103
A3
104
A4 <== Same product
105
A5
105
A4 <== Same product
Using GROUP BY ROLLUP yields a "Total" value of 5:
SELECT COALESCE(a.staff_name, 'Total') AS Staff_Name
, COUNT(DISTINCT b.product_id) AS [Count]
FROM staff a LEFT JOIN customer b ON a.cust_id=b.cust_id
GROUP BY ROLLUP (a.staff_name);
Results:
Staff_Name
Count
Billy
1
Peter
2
Tom
3
Total
5 **
Whereas calculating a simple sum of the totals, yields a "Total" value of 6. So just be aware of the difference.
Staff_Name
Count
Billy
1
Peter
2
Tom
3
Total
6 **
Original (Wrong Answer):
Can't remember where I saw this answer, but ... assuming Staff_Name is never null, you could use GROUP BY ROLLUP to obtain the total. That said calculating grand totals is usually more of a front-end job.
SELECT COALESCE(a.staff_name, 'Total') AS Staff_Name
, COUNT(DISTINCT b.product_id) AS [Count]
FROM staff a LEFT JOIN customer b ON a.cust_id=b.cust_id
GROUP BY ROLLUP (a.staff_name);
Try this one:
SELECT s.staff_name, COUNT(DISTINCT b.product_id), SUM(count) Total
FROM staff s
INNER JOIN customer b ON b.cust_id = s.cust_id
GROUP BY s.staff_name

Check if record from table exist then update or insert

I have a lot of tables that needed to merge but first I need to check if Student already in the table
Table 1 (English)
FirstName LastName BirthDate English
Kyle Fernandez 01/05/2002 82
Bill Cruz 08/24/2003 88
Table 2 (Math)
FirstName LastName BirthDate Math
Kyle Fernandez 01/05/2002 79
Bill Cruz 08/24/2003 83
Mae Sol 03/26/2002 87
Now what I need to do is merge those tables without repeating the same student. like this
Table Report Card
FirstName LastName BirthDate English Math
Kyle Fernandez 01/05/2002 82 79
Bill Cruz 08/24/2003 88 83
Mae Sol 03/26/2002 0 87
I need to insert and update in bulk. Thank you
I think you want a full outer join here:
SELECT
COALESCE(t1.FirstName, t2.FirstName) AS FirstName,
COALESCE(t1.LastName, t2.LastName) AS LastName,
COALESCE(t1.Birthdate, t2.Birthdate) AS Birthdate,
COALESCE(t1.English, 0) AS English,
COALESCE(t2.Math, 0) AS Math
FROM Table1 t1
FULL OUTER JOIN Table2 t2
ON t2.FirstName = t1.FirstName AND
t2.LastName = t1.LastName
The logic here is to retain a student even if it only appears in one, but not both, of the two tables. We report the English and math scores as zero in the case where a given student does not have any value in either table.

Find two rows of a column belongs to same row of another column

I have a table where I need to find list of subjects that have students from same department without using a subquery or Join
I tried to do the having count of department but it does not provide the output.
SELECT A.Subject,
B.StudentID,
B.DEPTID
FROM AUTHOR A , ACADEMIC B
WHERE A.StudentID = B.StudentID
GROUP BY B.DEPT,
A.Subject,
B.StudentID
Gives me the table output
Subject StudentID DEPT
1 100 100
1 101 100
2 102 100
3 103 100
3 104 100
I expect the output to give me the subject that has studentID from same department without using subquery or JOIN.

Get the top n rows from each group

I have a table as following:
[ID] [Name] [Score] [Class]
1 John 90 A
2 Mary 63 A
3 Tom 87 A
4 David 98 A
5 Mary 87 B
6 David 77 B
7 David 73 C
8 Mary 92 C
9 Tom 73 C
10 John 79 C
11 Mary 70 D
12 Jane 85 D
13 David 83 D
I need to get the top 2 persons based on the scores in each class.
My expected output is
[ID] [Name] [Score] [Class]
1 John 90 A
4 David 98 A
5 Mary 87 B
6 David 77 B
8 Mary 92 C
10 John 79 C
12 Jane 85 D
13 David 83 D
Here is what I tried so far but this is not producing the correct results
SELECT *
FROM Student s
WHERE
(
SELECT COUNT(*)
FROM Student f
WHERE f.name = s.name AND
f.score >= s.score
) <= 2
Use ROW_NUMBER:
SELECT
ID, Name, Score, Class
FROM(
SELECT *,
rn = ROW_NUMBER() OVER(PARTITION BY Class ORDER BY Score DESC)
FROM Student
) t
WHERE rn <= 2
DEMO
The specification said... "for each class"
The smallest change that would need to be made to the proposed query to get the the specified results "students with the two highest scores for each class", we'd need to replace one of the predicates in the correlated subquery... matching on [class] rather than on [name]...
SELECT s.*
FROM Student s
WHERE
(
SELECT COUNT(*)
FROM Student f
WHERE f.class = s.class
AND f.score >= s.score
) <= 2
Note that if there are multiple students in the class with the same "highest scores", the query will return all of those students, not just two students for class.
If we specifically want to return "at most two students from each class, the students who have the highest score in each class", we'd need to write the query a little differently.
Given the example data, with no duplicate scores in a class, the results would be the same.
The difference can be demonstrated by adding a row to the example data, for example, adding Saul, having the same "second highest" score as David.
11 Mary 70 D
12 Jane 85 D
13 David 83 D
14 Saul 83 D
The question we need to ask about the specification... should only two students be returned for this class, Jane has the highest score, so obviously return Jane. But David and Saul have the same score. Do we return both, or if we only return one of them, does it matter which one we return?
Should we return three rows:
[ID] [Name] [Score] [Class]
12 Jane 85 D
13 David 83 D
14 Saul 83 D
because those are all of the students with the two highest scores, or should we return just two of the students with the highest scores:
[ID] [Name] [Score] [Class]
12 Jane 85 D
13 David 83 D
or
[ID] [Name] [Score] [Class]
12 Jane 85 D
14 Saul 83 D
Once that question is answered, we can write a query that returns the specified result.
And (obviously) this isn't the only query. There are other query patterns that will return an equivalent result... using either ANSI-standard syntax, or vendor specific extensions.

list all students who have not taken midterm for 1 or more subjects

I have 3 tables likes Student,Subject,and Midterm tables.
Student table contains
studid Firstname lastname Class
1 A R 12A
2 B S 12A
3 C T 12A
4 D U 12A
5 E V 12B
SUBJECT table contains
subid subname
1 maths
2 science
3 english
MIDTERM table contains
studid subid marks examdate
1 1 100 2014-09-24
1 2 92 2014-09-25
1 2 92 2014-09-26
2 1 74 2014-09-24
2 2 78 2014-09-26
2 3 73 2014-09-26
3 1 90 2014-09-24
3 2 84 2014-09-25
3 2 92 2014-09-25
5 1 87 2014-09-24
4 2 79 2014-09-24
4 3 90 2014-09-26
The result must be:
Firstname LastName Subname
Based on the below comment and Assuming all Students must take all midterms
select Firstname , lastname , subname
from (
select studid , FirstName, lastname , subID , subname
from student , Subject) d
left outer join midterm m on d.studid = m.studID and d.subid = m.subid
where m.examdate is null
could probably write it without the catesian join but it should suffice
As I guess, the one who didn't pass a midterm is "the one who has less than 50 points".
If not, you can fix it in the last line.
You can use INNER JOIN SQL keyword to make a query to several logically interconnected tables definining a matching condition.
Simply said, first you need to understand how you will extract data logically.
For example, in your case you need to select from MIDTERM table as it stores both student id and subject id.
In words, it sounds like:
1. Take all results of failed midterms (midterms with marks < 50). Each row contains studid (who failed an exam) and subid (which exam he failed);
2. From table Student take FirstName and LastName of student who failed it;
3. From table Subject take Subname of subject which has been failed;
4. Return these three values.
In code, it looks like:
SELECT s.Firstname, s.Lastname, subj.subname FROM `MIDTERM`
FROM `MIDTERM` as m
INNER JOIN `Student` as s ON s.stuid = m.studid
INNER JOIN `SUBJECT` as subj ON subj.subid = m.subid
WHERE `marks` < 50

Resources