Get details semester wise highest marks without cursor - sql

I have two tables.
Student (Roll_id,Student_name)
Student_mark (roll_id, semester, marks, subject)
I want student details who has got highest marks IN semester AND subject wise WITH their roll_id,mark,semester,subject
using that two table.
can we get results without using cursor?.

You can use the RANK() function to put the students in order by mark (partitioned by semester and subject):
WITH RankedResults AS
( SELECT s.Roll_ID,
s.Student_Name,
sm.Semester,
sm.Subject,
sm.Marks,
StudentRank = RANK() OVER(PARTITION BY sm.Semester, sm.Subject ORDER BY sm.Marks DESC)
FROM Student s
INNER JOIN Student_Mark sm
ON s.Roll_ID = sm.Roll_ID
)
SELECT Roll_ID, Student_name, Semester, Subject, Marks
FROM RankedResults
WHERE StudentRank = 1;
Example on SQL Fiddle

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;

To display the name of the department that has the least student count in SQL

Tables:
Department (dept_id,dept_name)
Students(student_id,student_name,dept_id)
I am using Oracle. I have to print the name of that department that has the minimum no. of students. Since I am new to SQL, I am stuck on this problem. So far, I have done this:
select d.department_id,d.department_name,
from Department d
join Student s on s.department_id=d.department_id
where rownum between 1 and 3
group by d.department_id,d.department_name
order by count(s.student_id) asc;
The output is incorrect. It is coming as IT,SE,CSE whereas the output should be IT,CSE,SE! Is my query right? Or is there something missing in my query?
What am I doing wrong?
One of the possibilities:
select dept_id, dept_name
from (
select dept_id, dept_name,
rank() over (order by cnt nulls first) rn
from department
left join (select dept_id, count(1) cnt
from students
group by dept_id) using (dept_id) )
where rn = 1
Group data from table students at first, join table department, rank numbers, take first row(s).
left join are used is used to guarantee that we will check departments without students.
rank() is used in case that there are two or more departments with minimal number of students.
To find the department(s) with the minimum number of students, you'll have to count per department ID and then take the ID(s) with the minimum count.
As of Oracle 12c this is simply:
select department_id
from student
group by department_id
order by count(*)
fetch first row with ties
You then select the departments with an ID in the found set.
select * from department where id in (<above query>);
In older versions you could use RANK instead to rank the departments by count:
select department_id, rank() over (order by count(*)) as rnk
from student
group by department_id
The rows with rnk = 1 would be the department IDs with the lowest count. So you could select the departments with:
select * from department where (id, 1) in (<above query>);

Need to select different column for a query which requires group by clause

I have a Student table which contains following columns:
studentName, startYear, EndYear, classId, some more columns
This table contains startYear and EndYear for students of different class.
I want to write a query to find all the students name which took maximum years (diff b/w EndYear and startYear) to pass a class.
I want following three fields in select query
select studentName, classId, max(EndYear- startYear) as maxYears from Students group by classId;
but as group by doesn't contains studentname hence this query fails(and it make sense too).
Then I could do as :
Putting result of following query in temp table TEMP:
select classId, max(EndYear- startYear) from Students group by classId
and then join this temp table with student table.
select studentName, classId, EndYear- startYear from Student s join Temp t on s.classId = t.classId and (s.EndYear- s.startYear) = t.maxYears
But this doesn't look optimal to me. I am wondering what could be other ways to do it .
Try this query, which does a self-join to fetch the row with maximum (EndYear- startYear):
select s1.studentName, s1.classId, s1.EndYear-s1.startYear
from Student s1
inner join
(
select classId, max(EndYear- startYear)
from Students
group by classId
) s2
on s1.classId = s2.classId;
Try this:
SELECT *
FROM (
SELECT studentName, startYear, EndYear, classId,
DENSE_RANK() OVER(PARTITION BY classId ORDER BY endYear - startYear DESC) AS Rnk
FROM dbo.Student
) x
WHERE x.Rnk = 1;
The following script with correlated sub-query should be equivalent to the JOIN solution and even could be converted into JOIN by the SQL Server optimizer.
SELECT studentName, classId
FROM Students s
WHERE (EndYear- startYear) = (SELECT MAX(EndYear- startYear)
FROM Students sm
WHERE c.classId = sm.classId)

Trying to get the max from a group of aggregates

Here my schema: Exams(int students, int scores)
I am writing a SQL query to find the greatest spread between a student's score.
I have been able to generate a query that has the student id and their spread using this query:
select student, max(score) - min(score) from exams group by student;
Now this is where I am stumped. How do I get the maximum value of the spreads? More specifically, I don't really understand what to put in my select statement that would be outside my initial query.
You can also use a subquery:
select max(spread)
from (
select student, max(score) - min(score) as spread from exams group by student
) x;
Try this:
select student, max(score) - min(score) as SPREAD
from exams
group by student
order by (max(score) - min(score)) DESC
This will at least show the results from highest to lowest

How can I add an additional field to an SQL Group By clause

I have the following MS-Access SQL Table:-
NAME, SUBJECT, SCORE
..and I need to find the average score, highest score and the subject that the highest score was achieved against. I have managed to produce the following query but not sure how I can incorporate the SUBJECT field:-
SELECT NAME, Avg(SCORE) AS AverageScore, MAX(SCORE) AS best_score
FROM Scoretable
GROUP BY NAME
Any ideas ?
select sm.NAME, sm.AverageScore, sm.best_score, s.SUBJECT
from (
SELECT NAME, Avg(SCORE) AS AverageScore, MAX(SCORE) AS best_score
FROM Scoretable
GROUP BY NAME
) sm
inner join Scoretable s on sm.NAME = s.NAME
and sm.best_score = s.SCORE