Sum of Highest 5 numbers in SQL Server 2000 - sql

I am having a problem in query some data from database. My table is given below:
What i need is that sum of 5 highest total_marks from the table for each student.
Although i tried the code given below, but it is not returning what i expected.
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
Please help me guys. Thanking you advance.

You query could work if there's unique/primary key on (studentId, subcode). At the moment, the query returns 6 records instead of 5 for studentId = 1, for example, beause of duplicate subcode 303.
Usually table should have a unique key, may be you can add incremental id to rewrite your query like:
select s.*
from students as s
where
s.id in (
select top 5 a.id
from students as a
where a.studentId = s.studentId
order by a.total_marks desc
);
Or, if you have unique combinations of (studentId, subcode, total_marks), you can use query like this:
select s.*
from students as s
where
exists (
select *
from (
select top 5 a.subcode, a.total_marks
from students as a
where a.studentId = s.studentId
order by a.total_marks desc
) as b
where b.subcode = s.subcode and b.total_marks = s.total_marks
);
sql fiddle demo

First you should select top 5 grades for each student -
select row_number() over (partition by studentid order by total_marks desc) as rank,
studentid,
total_marks
from students
where rank <= 5
from there you'll be able to use this as a subquery, and use group_by and sum:
select studentid, sum(total_marks)
from
(
select row_number() over (partition by studentid order by total_marks desc) as rank,
studentid,
total_marks
from students
where rank <= 5
) t
group by studentid

This isn't ideal, but the method you started to use requires a primary key column. You can simulate one with a temp table since SQL 2000.
CREATE TABLE #temp (
StudentID INT,
total_marks INT,
ID INT Identity(1,1)
)
INSERT INTO #temp (
StudentID,
total_Marks
)
Select
StudentID,
total_marks
FROM Students
SELECT s.studentid, SUM(s.total_marks)
FROM #temp s
WHERE s.ID IN (SELECT TOP 2
a.ID
FROM #temp a
WHERE a.studentid = s.studentid
ORDER BY total_marks DESC)
GROUP BY studentid
I think SQL 2000 may have a slightly more compact syntax for this, but SQL Fiddle won't let me test versions that old.
Please test this carefully. You will be dumping this entire table to a temp table and that's almost always a bad idea.
Also, ensure that there is some combination of fields not including the total that uniquely identifies a row, or consider adding a surrogate key column to the table.
SQL Fiddle Demo

Related

Select the newest record

I would like to run a select statement that runs and select only the newest record by Recored_timestampe field for the keys teacher_id and student_id. So any time, it runs it needs to provide only one record. how could I do it, please? The output could be without the field Recored_timestampe. Thanks
Using the window function,partitioned by teacher_id and student_id and sorting it by recorded_timestamp will give you the desired result.
select * from(select teacher_id,student_id,teacher_name,comment ,recorded_timestamp, row_number() over(partition by teacher_id,student_id order by recorded_timestamp desc)as rownum from temp0607)out1 where rownum=1
Also you may have to look at the way recorded_timestamp is stored. If it is stored as string, you can convert it into timestamp using from_unixtime(unix_timestamp(recorded_timestamp,'dd/MM/yyyy HH:mm'),'dd/MM/yyyy HH:mm')
First, arrange the record by datetime
SELECT *,RANK() OVER (PARTITION BY student_id ORDER BY Recored_timestamp desc) as ranking
FROM #temp
Then, if you want to know the newest record with student_id which is not null, then you can use OUTER APPLY to add a column which is non-NULL student_id.
OUTER APPLY (SELECT student_id
FROM #temp
WHERE #temp.teacher_id = ranktable.teacher_id
AND student_id IS NOT NULL
) AS jointable
Here is an example:
Create Table #temp
(
teacher_id int
,student_id int
,teacher_name varchar(40)
,comment varchar(100)
,Recored_timestamp datetime
)
INSERT INTO #temp
VALUES
(449,111,'lucy','Could be better','2021-05-04 07:00:00.000')
,(449,null,'lucy','smily','2021-05-11 07:00:00.000')
,(449,111,'lucy','not listening','2021-05-08 07:00:00.000')
,(448,null,'Toni','Good','2021-06-04 09:00:00.000')
,(448,222,'Toni','not doing as expected','2021-06-04 08:00:00.000')
SELECT DISTINCT teacher_id,
jointable.student_id,
teacher_name,
comment,
Recored_timestamp,
ranking
FROM
(
SELECT *,RANK() OVER (PARTITION BY teacher_id ORDER BY Recored_timestamp DESC) AS ranking
FROM #temp
) AS ranktable
OUTER APPLY (SELECT student_id
FROM #temp
WHERE #temp.teacher_id = ranktable.teacher_id
AND student_id IS NOT NULL
) AS jointable
WHERE ranking = 1 --only newest record will be extracted
Drop table #temp
You can base from this query to get the newest data.
SELECT TOP 1 * FROM tablename T1
INNER JOIN(SELECT teacher_id, Max(Recored_timestamp) as MaxDate from tablename GROUP BY teacher_id) T2 ON T2.teacher_id = T1.teacher_id AND T1.Recored_timestamp = T2.MaxDate

How to get the largest value of a column in table?

I have the following table:
fileId, studentId,
Given a particular studentId, how can I get the largest fileId that is in the table ?
I saw this other query :
SELECT row
FROM table
WHERE id=(
SELECT max(id) FROM table
)
This would give the row where id is the largest. But what about the largest id for a given student id ? Is it better to match the student in the inner query or outer query ?
if you need the whole row:
select * from table where studentId = your_known_id order by fileId desc limit 1
This seems simplest:
SELECT studentId, MAX(ID)
FROM TableA
GROUP BY studentId
OR With filtering options:
WITH CTE AS
(
SELECT studentId, MAX(ID)
FROM TableA
GROUP BY studentId
)
SELECT * FROM CTE WHERE studentId ...
Try this:
SELECT *
FROM (
SELECT
id,
ROW_NUMBER () OVER (PARTITION BY studentId ORDER BY id desc) rnk
FROM table) a
WHERE a.rnk = 1;
... what about the largest id for a given student id
SELECT MAX(fileId)
FROM table
WHERE studentId = 123
Where 123 is the student if you want to filter on.
say there are three rows with studentId =3, but with fileId's = {4,5,6}, how do i get the row {fileId,studentId} = {6,3}
SELECT MAX(fileId), studentId
FROM table
WHERE studentId = 3
GROUP BY studentId
I think this code works as well
SELECT max(field) from table_name where studenId = <someID>

Getting Person having interests in more number of subjects - Sql Server

Sorry for the subject as it is not very definientia. I have 2 tables, one stores Person Data and one stores Subject data along with the person interested in. Two tables looks like below
Person
Id Name
1 Imad
2 Sumeet
3 Suresh
4 Navin
Subjects
Id PId Subject
1 1 DC
2 1 DS
3 3 DS
4 4 CA
PId is a Persons' Id
I need to get all students who are interested in max number of subjects, e.g Imad here.
Here is my query
With c as
(
select Pid, count(Id) as 'Total' from subjects group by Pid
)
select Pid into #Temp from c where Total = (Select Max(Total) from c)
select * from Person where Id in (Select Pid from #Temp)
It gives me desired output but whenever this type question is asked in interview, I never get good response from interviewer as they always expect better solution. I am not confident on my SQL skills that's why I think there must be more efficient solution hence I posted it here.
Thanks
Simply order the data and get top most one record with ties(this means if some students have equal counts they both will come in result):
select top 1 with ties p.Id, p.Name
from Subjects s
join Person p on s.PId = p.Id
group by p.Id, p.Name,
order by count(*) desc
You can try this :
;With c as
(
select Pid, count(Id) as 'Total' from subject group by Pid
)
select * from Person join c on c.Pid=Person.Id where c.total>1
Try to this
select * from person
where id in(
Select b.pid
from subject b
group by b.pid
having count(b.pid)>1
)
declare #t table (ID int,name varchar(10))
insert into #t (ID,name)values (1,'imad'),(2,'sumeet'),(3,'suresh'),(4,'navin')
declare #tt table (Id int,Pid int,Subject varchar(10))
insert into #tt (Id,Pid,subject)values (1,1,'DC'),(2,1,'DS'),(3,3,'DS'),(4,4,'CA')
select p.ID,P.name,ttt.Subject from (Select P.ID,P.name,P.Cnt from (
select t.ID,t.name,COUNT((t.ID))Cnt from #t t
INNER JOIN
#tt tt ON t.ID = tt.Pid
GROUP BY t.ID,t.name)P
GROUP BY P.Cnt,P.ID,p.name
HAVING(cnt) > 1)P
INNER JOIN #tt ttt ON Ttt.Pid = P.ID
The current solutions with TOP are MS SQL Server specific. Following solution is based on Standard SQL's Windowed Aggregate Functions, which most DBMSes support:
select Pid, Total
from
(
select Pid, count(Id) as Total
,rank() over (order by count(Id) desc) as rn
from subjects group by Pid
) as dt
where rn = 1

Select rows of joined values using MAX

Consider the following schema.
Student:
StudentID uniqueidentifier
Name varchar(max)
FKTeacherID uniqueidentifier
TestScore:
TestScoreID uniqueidentifier
Score int
FKStudentID uniqueidentifier
My goal is to write a query that yields each teacher's highest test score and the student that achieved it. Returning the teacher's id (Student.FKTeacherID), the score that was achieved (TestScore.Score) and the student that achieved it (Student.Name).
I can write something like this to get the first two required columns:
SELECT FKTeacherID, MAX(Score) MaxScore
FROM Student
JOIN TestScore on FKStudentID = StudentID
GROUP BY FKTeacherID
But I can't obtain the relevant Student.Name without adding it to the group by clause, which would change the result set.
If I'm understanding correctly, one option is to use row_number() -- here's an example with a common-table-expression:
with cte as (
select s.fkteacherid,
ts.score,
s.name,
row_number() over (partition by s.fkteacherid order by ts.score desc) rn
from student s
inner join testscore ts on s.studentid = ts.fkstudentid
)
select fkteacherid, score, name
from cte
where rn = 1
The basic idea is to group by fkteacherid, ordering by the score desc within each group, and taking the first record from each group.
UPDATE
Please try the updated query if you might:
SELECT
FKTeacherID, Name, Score
FROM
Student
JOIN TestScore on FKStudentID = StudentID
JOIN
(
SELECT
B.FKTeacherID AS TeacherID, MAX(A.Score) MaxScore
FROM
Student B
JOIN TestScore A on A.FKStudentID = B.StudentID
GROUP BY
B.FKTeacherID
) As TeachersMaxScore
ON TeachersMaxScore.TeacherID = FKTeacherID
AND TeachersMaxScore.MaxScore = Score

Get the top row after order by in Oracle Subquery

I have a table student(id, name, department, age, score). I want to find the youngest student who has the highest(among the youngest students) score of each department. In SQL Server, I can use following SQL.
select * from student s1
where s1.id in
(select s2.id from student s2
where s2.department = s1.department order by age asc, score desc top 1).
However, in Oracle, you cannot use the order by clause in subquery and there is no limit/top like keyword. I have to join the student table with itself two times to query the result. In oracle, I use following SQL.
select s1.* from student s1,
(select s2.department, s2.age, max(s2.score) as max_score from student s2,
(select s3.department, min(s3.age) as min_age from student s3 group by s3.department) tmp1 where
s2.department = tmp1.department and s2.age = tmp1.min_age group by s2.department, s2.age) tmp2
where s1.department =tmp2.department and s1.age = tmp2.age and s1.score=tmp2.max_score
Does anyone have any idea to simplify the above SQL for oracle.
try this one
select * from
(SELECT id, name, department, age, score,
ROW_NUMBER() OVER (partition by department order by age desc, score asc) srlno
FROM student)
where srlno = 1;
In addition to Allan's answer, this works fine too:
select *
from (SELECT *
FROM student
order by age asc,
score desc)
where rownum = 1;
In addition to Bharat's answer, it is possible to do this using ORDER BY in the sub-query in Oracle (as point out by Jeffrey Kemp):
SELECT *
FROM student s1
WHERE s1.id IN (SELECT id
FROM (SELECT id, ROWNUM AS rn
FROM student s2
WHERE s1.department = s2.department
ORDER BY age ASC, score DESC)
WHERE rn = 1);
If you use this method, you may be tempted to remove the sub-query and just use rownum = 1. This would result in the incorrect result as the sort would be applied after the criteria (you'd get 1 row that was sorted, not one row from the sorted set).
select to_char(job_trigger_time,'mm-dd-yyyy') ,job_status from
(select * from kdyer.job_instances ji INNER JOIN kdyer.job_param_values pm
on((ji.job_id = pm.job_id) and (ji.job_spec_id = '10003') and (pm.param_value='21692') )
order by ji.job_trigger_time desc)
where rownum<'2'