Sum function in sql query - sql

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;

Related

SQL Select column which is not used in select section of subquery which find duplicates

I am trying to find in my database records which has duplicated fields like name, surname and type.
Example:
SELECT name, surname, type, COUNT(*)
FROM customers
GROUP BY name, surname
HAVING COUNT(*)>1
Query results:
Robb|Stark|1|2
Tyrion|Lannister|1|3
So we have duplicated customer with name and surname "Robb Stark" 2 times and "Tyrion Lannister" 3 times
Now, I want to know the id of these records.
I found similar problem described here:
Finding duplicate values in a SQL table
there is answer but no example.
Use COUNT as an analytic function:
WITH cte AS (
SELECT *, COUNT(*) OVER (PARTITION BY name, surname) cnt
FROM customers
)
SELECT * -- return all columns
FROM cte
WHERE cnt > 1
ORDER BY name, surname;
The simplest way will be to use the EXISTS as follows:
SELECT t.*
FROM customers t
where exists
(select 1 from customers tt
where tt.name = t.name
and tt.surname = t.surname
and tt.id <> t.id)
Or use your original query in IN clause as follows:
select * from customers where (name, surname) in
(SELECT name, surname
FROM customers
GROUP BY name, surname
HAVING COUNT(*)>1)
If you want one row per group of duplicate, with the list of id in a comma separated string, you can just use string aggration with your existing query:
SELECT name, surname, COUNT(*) as cnt,
STRING_AGG(id, ',') WITHIN GROUP (ORDER BY id) as all_ids
FROM customers
GROUP BY name, surname
HAVING COUNT(*) > 1

find names of classes with the highest enrollement

I have the following relation in database
ENROLLMENT (snum: int, cname:varchar(100))
where snum is the student id and cname is the course name
and I want to find the name(s) of the class(s) with the most students enrolled.
There are multiple solutions. One solution is usage of SELECT TOP 1 :
SELECT TOP 1
cname, count(*) as NumberOfStudents
FROM ENROLLMENT
GROUP BY cname
ORDER BY count(*) desc
Use a common table expression (cte):
with cte as
(
select cname, count(*) as cnt
from ENROLLMENT
group by cname
)
select cname
from cte
where cnt = (select max(cnt) from cte)

Removing redundancies in sql query that contains subquery

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;

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)

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