select sum of max in sqlite - sql

Based off the following table schema, I want to be able to select the sum of the maximum of each submission. A student can have multiple submissions for a contest, so their total score is the sum of the maximum score of each contest. Right now my query selects student_id, name, and maximum score of all their contests, how can I get the sum of the max of all their contest? The expected output based on the input should be:
select student_id, name, max(score) as TotalScore
from students2 as st
join submissions as s
where st.student_id = s.student_id
group by st.student_id, name
having count(*) > 1
order by TotalScore desc;

select student_id, name, sum(TotalScore) total_sum
from (select st.student_id student_id
, name
, max(score) as TotalScore
from students as st
join submissions as s
on st.student_id = s.student_id
group by s.contest_id, s.student_id)
group by student_id;
Here is a demo
I have started to create a demo before your sample data...
From your text in your question I did not understood what do you need having count(*) > 1 for so I did not used it.
Please note: I have used students as the name of the table and not students2.

Use two levels of aggregation:
select student_id, name, sum(max_score) as TotalScore
from students2 st join
(select s.student_id, max(score) as max_score
from submissions s
group by s.student_id
) s
on st.student_id = s.student_id
group by st.student_id, name
having count(*) > 1;
order by TotalScore desc;
I notice that your FROM clause is using JOIN but missing the ON clause. You should get used to always including that.

Related

What is the alternative for ALL and EVERY in SQLite?

Hey im new to SQL and I'm trying to find the names of students enrolled in the maximum number of classes using SQLite on sqliteonline.com
These are the relations:
This is what I have:
SELECT S.sname
FROM Student S
WHERE S.snum IN (SELECT E.snum
FROM Enrolled E
GROUP BY E.snum
HAVING COUNT (*) >= ALL (SELECT COUNT (*)
FROM Enrolled E2
GROUP BY E2.snum ))
AND this is the error that I get:
I'm guessing the ALL keyword doesn't exist in SQLite or is used differently so I'm wondering what I should do next.
I'm also having the same problem with the EVERY keyword.
What I'm trying to solve:
For each faculty member that has taught classes only in room R128, print the faculty
member's name and the total number of classes she or he has taught.
What I have:
SELECT F.fname, COUNT(*) AS CourseCount
FROM Faculty F, Class C
WHERE F.fid = C.fid
GROUP BY F.fid, F.fname
HAVING EVERY ( C.room = "R128" )
What I get:
You can use ORDER BY and LIMIT:
SELECT S.sname
FROM Student S
WHERE S.snum IN (SELECT E.snum
FROM Enrolled E
GROUP BY E.snum
HAVING COUNT(*) = (SELECT COUNT(*)
FROM Enrolled E2
GROUP BY E2.snum
ORDER BY COUNT(*) DESC
LIMIT 1
)
);
Note: There are other ways to express the query logic. This specifically addresses the question that you asked.
One option uses window functions:
select s.name
from student s
inner join (
select snum, rank() over(order by count(*) desc) rn
from enrolled e
group by snum
) e on e.snum = s.snum
where rn = 1
If your version of SQLite does not support window functions (which were added in version 3.25), I would recommend a join and filtering with a having clause:
select s.name
from students s
inner join enrolled e on e.snum = s.snum
group by s.snum, s.name
having count(*) = (
select count(*)
from enrolled
group by snum
order by count(*) desc limit 1
)

How to find least group of students

I have this tables:
Student - Id, FirstName, LastName, Age
Group - Id, Name
Student_Group - Student_Id, Group_Id
I need to find least group of student. I have tried many times. I would be so glad if somebody helped.
Have you tried to do
SELECT top 1 g.name
FROM group g INNER JOIN Student_group sg ON g.id = sg.Group_Id
WHERE count(sg.student_id) >0
ORDER BY count(sg.student_id)
GROUP BY sg.group_id
?
If you want also the groups with 0 students you should to do
SELECT top 1 g.name
FROM group g INNER JOIN Student_group sg ON g.id = sg.Group_Id
ORDER BY count(sg.student_id)
GROUP BY sg.group_id
--If you just need the group with the least members,
--Group By and Count will work to find the Group with the least members
--Then use select top 1 record and order by GroupCount Ascending
SELECT TOP 1 Group_Id, COUNT(Group_Id) AS [GroupCount]
FROM Student_Group
GROUP BY Group_Id
ORDER BY [GroupCount]

Write a query to get name and marks of second highest scorer for every course

Write a query to get name and marks of second highest scorer for every course.
This will give you second highest marks
Select
(SELECT MAX(Marks) FROM Marks
WHERE Marks NOT IN
(SELECT MAX(Marks) FROM Marks)) AS 'Second Highest'
from Marks
WITH MarksCTE AS
(
Select DENSE_RANK() OVER (PARTITION BY CourseID ORDER BY Marks desc) as Rank, m.Marks,c.Name as Course,s.Name as Student
From Marks as M
Inner Join Student as s on S.Id = M.StudentID
Inner Join Course as c on c.Id = M.CourseID
)
Select Marks,Course,Student from MarksCTE where Rank = 2

Count within a count

REVISED:
Okay, thanks to all of your input, I figured out what I was doing wrong (sorry guys). I am grabbing the courseID which counts as a section NOT an actual course. For me to grab the actual courseName, I have to go over to the Course Table.
So now I have StudentID from Enrollment and CourseNum from Course that need to be used to count. I'll try and work with what you guys have provided to see if I can come up with the results.
EDIT:
Here's my revised SQL. This provides me with the total courses for each student. I'm getting there:
SELECT Count(DISTINCT Course.courseNum), Grades.studentID
FROM Grades INNER JOIN
Course ON Grades.courseID = Course.courseID
GROUP BY Grades.studentID;
Final Code just in case those who care:
SELECT COUNT(NumCourses) FROM
(SELECT Count(DISTINCT Course.courseNum)AS NumCourses
FROM Grades INNER JOIN
Course ON Grades.courseID = Course.courseID
GROUP BY Grades.studentID
HAVING Count(DISTINCT Course.courseNum) = 1) a;
Try this:
select count(studentId), count(courseId) from enrolment group by courseId having (count(courseId) = 2);
To get the total number of students on 4+ courses, this is the SQL:
SELECT COUNT(CourseCount) AS CourseCount
FROM (
SELECT StudentID, COUNT(CourseID) AS CourseCount
FROM enrollment
GROUP BY StudentID
HAVING (COUNT(CourseID) >= 4)) AS T
It is much simpler to get the student count using a second query.
SELECT COUNT(NumCourses) AS NumStudents, NumCourses FROM (SELECT COUNT(courseID) AS NumCourses FROM enrollment GROUP BY courseID HAVING COUNT(courseID) = 4) As SomeTableAlias
select count(*)
from (select student_id from enrollment group by student_id having count(*) = 4)
The inner query gives you the IDs of the students who enrolled in exactly 4 courses then I count them.
I thought you wanted the number of students who are enrolled in exactly four courses.
If you want to count the number of students and the number of courses, do this:
SELECT * FROM (SELECT COUNT(*) AS NumStudents FROM (SELECT DISTINCT studendid FROM enrollment)), (SELECT COUNT(*) AS NumCourses FROM (SELECT DISTINCT courseid FROM enrollment))
SELECT COUNT(*) FROM
(SELECT COUNT(*) FROM enrollment
GROUP BY studentid HAVING COUNT(*) = 4)

SQL AVG AND JOIN ORACLE

I have the following task: Find the students with the average of grades greater than the average of all grades (All the grades of all the students are on the same column). Moreover I have to get from a different table his name.
The first table looks like this:
Student_ID GRADE Course_Name
50 10 Math
60 9 Math
100 10 Math
200 7 Math
50 8 Sport
100 7 Sport
and so on...
And in the second table:
Student_ID Name
50 JHON
60 Mark
100 FIONA
200 ROBERT
I get my head tied up in the order of the things I should do. Can you help me with the sql oracle code and give me an explanation on how you thought in the process? Thank you in advance.
Here is an option using analytic functions:
SELECT t.*
FROM
(
SELECT s.*, g.*,
AVG(g.GRADE) OVER (PARTITION BY s.Student_ID) avg_s,
AVG(g.GRADE) OVER () avg_all
FROM students s
LEFT JOIN grades g
ON s.student_ID = g.student_ID
) t
WHERE avg_s > avg_all
Edit:
If you just want to report each student alone who meets the grade criteria you may try doing SELECT DISTINCT Name in the outer select.
To get the average grade of each student join the two tables and GROUP BY the student ID and name. To get the average of all the grades you need a separate query, because it's a different qranularity. You can use this as a sub-query in the HAVING clause of the GROUP BY.
select g.student_id
, s.name
, avg(g.grade) as student_avg
from grades g
join students s
on g.student_id = s.student_id
group by g.student_id, s.name
having avg(g.grade) > ( select avg(grade) from grades);
Here is a SQL Fiddle demo.
One solution is
select s.student_id, s.name, avg(g.grade)
from grades g
inner join students s on s.student_id = g.student_id
group by s.student_id, s.name
having avg(g.grade) > (select avg(grade) from grades)
On the thought process:
First, you need to realize that the names are not important for the task, you can just join them with the result of the average computation as last step. So focus on that one.
SELECT name, avg_grade
FROM ( <query to get the averages> ) q
JOIN students ON q.student_id = name_table.student_id
Second, Average of all grades is just one number, it can go in a subselect in a WHERE or HAVING condition. It also is just
SELECT AVG(grade) from grades
Third, you need the average grade of every student. This can be achieved with GROUP BY.
SELECT student_id, AVG(grade)
FROM grades
GROUP BY student_id
Tying everything together:
SELECT name, avg_grade
FROM (
SELECT student_id, AVG(grade) as avg_grade
FROM grades
GROUP BY student_id
HAVING AVG(grade) > (SELECT AVG(grade) from grades)
) q
JOIN students ON q.student_id = name_table.student_id