Removing redundancies in sql query that contains subquery - sql

Suppose we have a table with scheme
student(id (primary key), name, math_score, english_score)
I am trying to get student information (id and name) with highest rank (ordered by highest sum of math score and english score). There may be several student with tie, and we want all of them. The way I thought about doing this is to use subquery to get a table with sum of scores, then find ids, names that have highest sum.
SELECT s.id, s.name
FROM (SELECT s.id, s.name, s.math_score+s.english_score as sum
FROM student s) s
WHERE s.sum = (SELECT max(s.sum)
FROM (SELECT s.id, s.name, s.math_score+s.english_score as sum
FROM student s) s)
This works, but seems very redundant and not efficient.
I just started learning sql language, and I would appreciate some insight on this problem!

Use WITH TIES
create table #student(
id int primary key identity(1,1),
name varchar(50),
math_score decimal,
english_score decimal
)
insert into #student
values
('Tom', 90, 90),
('Dick', 70, 70),
('Harry', 80, 100)
select TOP(1) WITH TIES
id,
name,
math_score,
english_score,
math_score + english_score as ScoreRank
from #student
order by
math_score + english_score desc
Gives the answer:
id|name|math_score|english_score|ScoreRank
1|Tom|90|90|180
3|Harry|80|100|180

This should accomplish it, you're adding in an unnecessary step.
select id,
name,
math_score+english_score as total_score
from student
where math_score+english_score=(select max(math_score+english_score)
from student)

SELECT id, name, math_score+english_score as 'sum'
FROM student
Order by math_score+english_score DESC;

Related

Optimize Simple SQL Query

Suppose you have a table of students and a gpa. The idea is return the student or students with the highest GPA. If only 1 student, the prize is $1000. Otherwise, the amount is split between the number of students sharing the highest gpa. The result below returns what I would expect, 3 students, and an amount of 333. I'm wondering if this is the best or most optimal way of writing the query?
CREATE TABLE Test (
PersonID int,
Name varchar(255),
GPA DECIMAL(3,2)
);
INSERT INTO Test(personid, name, gpa) VALUES(1, 'Frank', 2.7)
INSERT INTO Test(personid, name, gpa) VALUES(2, 'Barb', 3.7)
INSERT INTO Test(personid, name, gpa) VALUES(3, 'Tammy', 3.7)
INSERT INTO Test(personid, name, gpa) VALUES(4, 'Edward', 3.7)
Select name, gpa,
(Select Case When Count(*) = 1 Then '1000'
Else 1000/COUNT(*)
End
FROM Test
WHERE gpa = (SELECT MAX(gpa) FROM test)
) As 'Prize Amount'
FROM Test
Where gpa = (SELECT MAX(gpa) FROM test)
Results of query
I feel like it isn't efficient because of having to query twice. I'd like to just be able to divide by the number of rows. Something like below doesn't work (groupby issue) and adding a groupby on gpa, name would always display 1000, since each group of name/gpa has 1 record.
Select name, gpa,
Case When Count(*) = 1 Then '1000'
Else 1000/COUNT(*)
End As 'Prize Amount'
FROM Test
Where gpa = (SELECT MAX(gpa) FROM test)
I think you want window functions:
select t.*,
1000.0 / count(*) over ()
from t
where t.gpa = (select max(t2.gpa) from test t2);
With an index on gpa, this is probably the fastest solution.

Sum function in sql query

I have this table Student :
Id (int , primary key identity)
Name (varchar(20))
Score (int)
RecordDate (DateTime)
I need to select all the columns of this table plus an extra column that represents the 'Sum' of 'Score' column depending of the Name of the student.
I tried this but it didn't work
select S.Id,S.Name ,S.Score ,S.RecordDate, ( select Sum(Score) from Student where Name= S.Name) as All_Student_Score
from Student S;
How can I change this query ?
You can use a `JOIN`:
SELECT S.*,
T.All_Student_Score
FROM Student S
INNER JOIN (SELECT Name, SUM(Score) All_Student_Score
FROM Student) T
ON S.Name = T.Name;
You can try like this
select Id,Name ,Score ,RecordDate, sum(score) over(partition by name) as All_Student_Score from Student S
The below solution works for your requirement.
select Id,
Name,
Score,
RecordDate,
sum(score) over( partition by name ) as All_Student_Score
from Student;
Because no one showed you that your own solution should work if you just alias your table in your sub query you can do the following:
select
S.Id,S.Name
,S.Score
,S.RecordDate
,(select Sum(Score) from Student s2 where s2.Name= S.Name) as All_Student_Score
from
Student S;

find MIN without using min()

I am trying to find student who has min score which will be the result of the below query. However, I was asked to write the query without using MIN(). Spent several hours but I can't find the alternative solution :'(.
select s.sname
from student s
where s.score =
(select min(s2.score)
from score s2)
This is one way, which will work even if two students have same lowest score.
SELECT distinct s1.sname
FROM student s1
LEFT JOIN student s2
ON s2.score < s1.score
WHERE s2.score IS NULL
The below is the method using limit, which will return lowest score student, but only one of them if multiple of them have same score.
select sname
from student
order by score asc
limit 1
Here's a possible alternative to the JOIN approach:
select sname from student where score in
(select score from student order by score asc limit 1)
create table student (name varchar(10), score int);
insert into student (name, score) values('joe', 30);
insert into student (name, score) values('jim', 88);
insert into student (name, score) values('jack', 22);
insert into student (name, score) values('jimbo', 15);
insert into student (name, score) values('jo bob',15);
/* folks with lowest score */
select name, score from student where not exists(select 1 from student s where s.score < student.score);
/* the actual lowest score */
select distinct score from student
where not exists(select 1 from student s where s.score < student.score);
Note that not exists can be brutally inefficient, but it'll do the job on a small set.
One way of doing it would be to Order the results in Ascending order and take the first row.
But if you are looking at a more generic solution as a student will have more than one mark associated with him, So you need to find the total marks for each student and then find the student with the least total.
This is the first scenario, A student only has one row in the table.
CREATE TABLE Student
(
SLNO INT,
MARKS FLOAT,
NAME NVARCHAR(MAX)
)
INSERT INTO Student VALUES(1, 80, 't1')
INSERT INTO Student VALUES(2, 90, 't2')
INSERT INTO Student VALUES(3, 76, 't3')
INSERT INTO Student VALUES(4, 98, 't4')
INSERT INTO Student VALUES(5, 55, 't5')
SELECT * From Student ORDER BY MARKS ASC
The second scenario as specified above is, He has multiple rows in the table, So we insert two more rows into the table for existing users.
Then we select the users by taking the sum of their marks grouping the results by name and then ordering the results by their total
INSERT INTO Student VALUES(6, 55, 't1')
INSERT INTO Student VALUES(6, 90, 't5')
SELECT SUM(MARKS) AS TOTAL, NAME FROM Student
GROUP BY NAME
ORDER BY TOTAL
Hope the above is what you are looking for.
You can try stored procedure to find student with minimum score.

SQL statement to return student's best grade

I have a table as follows with student's exam results in:
I want a SQL statement that will return the student's best grade:
Student 1 - Maths A
Student 2 - Maths D
etc.....
I have tried MAX() on the date, DISTINCT, Grouping By... But I think I'm fumbling in the dark.
EDIT: I should say that MIN and MAX on the grade will not work as A* is a possible grade and alphabetically comes after A which is incorrect. Grades could be a variety of letters and numbers which have no logical ranking (e.g. L1 is better than EL1 but L3 is better than L1). Possibly a subtable with ranking is required?
SELECT StudentName
,MIN(Grade) Best_Grade
FROM Table_Name
GROUP BY StudentName
Please try below query in SQL :
create table #test_table(
name varchar(20),
subjects varchar(20),
grade varchar,
resultdate datetime
)
insert into #test_table
select 'student1', 'math', 'A','2012-09-01' union all
select 'student1', 'math', 'B','2013-09-01' union all
select 'student2', 'math', 'D','2014-09-01' union all
select 'student1', 'math', 'A','2014-09-01'
select * from(
select row_number() over(partition by name,subjects order by grade,resultdate desc) as rn,* from #test_table ) tbl where rn=1
drop table #test_table

Is this sql query correct? If incorrect how can I fix it?

Schema:
Student(studentid,name,age)
Course(coursename,dept)
enroll(studentid,course,grade)
I need to find , for students in each age group find their average grade for courses they have taken for Political Science and History, and return the names of student with max average grade for each age group
My attempt so far is :
select max(grade), age, name
from (
select name, age, grade
from student s, (
select avg(grade) as grade, e.studentid
from enroll e
where dname in ('Political Sciences', 'History')
group by studentid
) as temp
where s.studentid = temp.studentid
) temp1
group by temp1.age;
I want to know if logically it is correct, and not syntactically.
Here's a few tips regarding your query:
Be careful with your table aliases. Make sure that you carry them over to your SELECT
You can only include columns in your SELECT that are being used in your aggregate (GROUP BY). Therefore, you can't GROUP BY temp1.age and SELECT age, name
The logic behind your SQL looks solid to me, so long as "Age" correlates to "Age Group", and does not refer to the individual student's age.