Get top records for each group in sql - sql

I have a table which has 3 columns StudentID, Score and ClassNumber.
What I need now is to select top 5 students of each Class (according to their Score result).
For example if there are 20 students in Class1, 40 students in Class2 and students in Class3, I need to select 15 top score students for each Class(5 for Class1, 5 for Class2, 5 for Class3)
How can I do this in one SQL query?

Do you mean somthing like this?
with tmp as
(
select ClassNumber,
StudentID,
Score,
row_number() over (partition by ClassNumber order by Score desc) row_num,
from Student s
)
select ClassNumber, StudentID, Score
from tmp
where row_num <= 5
order by ClassNumber

Solution in MYSQL :
SELECT StudentID, Score, ClassNumber
FROM
(SELECT StudentID, Score, ClassNumber,
#class_rank := IF(#current_class = classNumber, #class_rank + 1, 1) AS class_rank,
#current_class := ClassNumber
FROM student
ORDER BY ClassNumber, score DESC
) ranked
WHERE class_rank <= 5;
Solution in SQL SERVER:
select ClassNumber, StudentID, Score
from (
select ClassNumber,
StudentID,
Score,
dense_rank() over (partition by ClassNumber order by Score desc) ranking
from Student s
) as t
where ranking <= 5
order by ClassNumber

Related

Query without partition by or functions like rank()

Suppose we have the table students (name, grade, group, year)
We want a query that ranks for each group the corresponding students.
I know that this can be done easy with rank() OVER ( partition by group order by grade DESC ). But I think that this can also be done with a self join or a subquery. Any ideas?
The equivalent to rank() is:
select s.*,
(select 1 + count(*)
from students s2
where s2.group = s.group and
s2.grade > s.grade
) as rank
from students s;

SQL query to get instructors and students has invalid identifier

Hi I have a schema that look like this
I made two queries that had to do this:
Find the names of the top 4 instructors who have taught the most number of distinct courses. Display also the total number of courses taught.
Output columns: InstructorName, NumberOfCoursesTaught
Sort by: NumberOfCoursesTaught in descending order
Find the top 2 students who have taken the most number of courses.
Output columns: S_ID, StudentName, NumberOfCourses
Sort by: NumberOfCourses in descending order
For query 1, I wrote:
SELECT name AS InstructorName, count(course_id) AS NumberOfCourses
FROM Teaches
WHERE name IN (SELECT name FROM Instructor where Instructor.i_id = Teaches.i_id)
GROUP BU i_id
ORDER BY COUNT(course_id) DESC;
For query 2, I wrote
SELECT s_id as S_ID, name as StudentName, count(course_id) as NumberOfCourses
FROM Takes
WHERE name IN (SELECT name FROM Student WHERE Takes.s_id = Student.s_id)
GROUP BY s_id
ORDER BY COUNT(course_id) DESC;
Both say:
"NAME" Invalid identifier
I suggest that you should use another logic to build your queries. Here is a demonstration for the first query ; from there on, you should be able to create the second query (and maybe post it as an answer?).
Start with an aggregate query that computes the number of teaches per instructor id, looking at the Teaches table:
SELECT i_id, COUNT(*) cnt FROM Teaches GROUP BY i_id
Then rank each record by decreasing count, using window function ROW_NUMBER() :
SELECT i_id, cnt, ROW_NUMBER() OVER(ORDER BY cnt DESC) rn
FROM (SELECT i_id, COUNT(*) cnt FROM Teaches GROUP BY i_id) t
All that is left to do is get thte instructor name (JOIN ON Instructor) and filter in the top 4 records
SELECT i.name InstructorName, x.cnt NumberOfCoursesTaught
FROM (
SELECT i_id, cnt, ROW_NUMBER() OVER(ORDER BY cnt DESC) rn
FROM (SELECT i_id, COUNT(*) cnt FROM Teaches GROUP BY i_id) t
) x
INNER JOIN Instructor i ON i.i_id = x.i_id
WHERE x.rn <= 4
ORDER BY x.cnt desc

Top 2 Salary Grouped By Department

Below is the table I am referring to.
I want to find ou the 2 Employees in each department with highest salary.
Further to the above answer, if there are ties (multiple employees sharing the same salary), you can use the following to bring them all through instead of just picking two at random (which is what the ROW_NUMBER clause will do)
SELECT *
FROM (
SELECT *, DENSE_RANK() OVER (PARTITION BY Dept ORDER BY Salary DESC) AS rn
FROM MyTable ) t
WHERE t.rn <= 2
Use ROW_NUMBER() to get the top salaries per Department, then select the first two records from each departmental partiton:
SELECT *
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY Dept ORDER BY Salary DESC) AS rn
FROM MyTable ) t
WHERE t.rn <= 2

How to aggregate top 5 numbers in SQL Server 2000

Hi i I am facing a problem, it is that the students can have more than 5 subjects but i have to sum of only 5 subjects total marks of which the student secured the highest. One way to say sum of top 5 total marks obtained by any student.
How do i proceed please help me. Thanks in advance.
In SQL 2000 you will need to use a subselect to determine how many rows with the same ID have a higher mark. Then Filter for rows that have less then 5 higher marked rows above it:
select
ID, Sum(Mark)
From Table1 t
where
(Select count(*)
from Table1 it
where it.id=t.id and it.mark>t.mark) <5
group by ID
ROW_NUMBER isn't in sql-server-2000 unfortunately. You can achieve the same result with a subquery though. Hopefully this is what you're looking for:
SELECT s.studentid, SUM(s.total_marks)
FROM students s
WHERE s.sub_code IN (SELECT TOP 5 sub_code
FROM students a
WHERE a.studentid = s.studentid
ORDER BY total_marks DESC)
GROUP BY studentid
Working in fiddle
Here is a query that gives you only the 5 hightest marks per student:
SELECT studentID, total_marks,
row_number() OVER (PARTITION BY studentID, ORDER BY total_marks DESC) as rowN
FROM studentTable
WHERE rowN <= 5
So to get the total:
SELECT studentID, SUM(total_marks)
FROM
(
SELECT studentID, total_marks,
row_number() OVER (PARTITION BY studentID, ORDER BY total_marks DESC) as rowN
FROM studentTable
WHERE rowN <= 5
) T
GROUP BY studentID

Get MAX value of count of each dept after group by clause on dept and grade

My query gets count of employee for each dept for each grade .
select dept , grade , count(1) CNT
from mytable
group by dept , grade
order by dept , cnt desc;
now i need from that the grades getting max count of each dept .
output should be
dept grades MAX(count)
how can i do that?
Thanks
The best way to do this is using the row_number() function:
select dept, grade, cnt
from (select dept, grade, count(*) as cnt,
row_number() over (partition by dept order by count(*) desc) as seqnum
from mytable t
group by dept, grade
) t
where seqnum = 1