How to list all students who got above average grade of their group in SQL table? We have 6 group_ids so there six different average grades.
group_id student grade
1 James 85
1 Adam 96
2 Tom 56
2 Jane 89
2 Anny 90
Result:
group_id student grade
1 Adam 96
2 Jane 89
2 Anny 90
ashkufaraz's answer is closer but not quite right
select group_id,student,grade from students one where grade >
(select avg(grade) from students two where two.group_id = one.group_id)
The question is just tagged SQL, so this is an answer using standard SQL:
One option is to use a window function:
select group_id,student,grade
from (
select group_id,student,grade,
avg(grade) over (partition by group_id) as group_avg
from studends
) t
where grade > group_avg;
This has the additional benefit that you can also display the group average along with the result with no additional join or sub-select.
Related
How to calculate percentage of students with higher mark than average for each course?
Assume I have a table (avg_marks) with average marks for each course and number of students in the course:
course_id avg_mark num_students
12345 74 20
12346 70 17
12347 64 33
...
I also have a table (enrolments) with all courses, students enrolled in those courses and their mark for the course:
course_id student mark
12345 1010 63
12345 2111 75
12345 3221 85
12345 6788 40
...
12347 8989 90
...
The expected output would be the table with course id and percentage of students with higher marks than average:
course_id percentage
12345 40
12345 20
12346 50
...
I have calculated number of students who have higher mark than average, but somehow I wasn't able to calculate the percentage (perhaps because the table contains all courses?). How can I modify this query or make a new one to calculate the percentage of students with higher mark than average?
Number of students with higher than average mark:
SELECT e.course_id, COUNT(e.student)
FROM enrolments e, avg_mark av
WHERE e.mark > av.avg_mark AND e.course_id=av.course_id
Output of the above query was like the following:
course_id count
12345 5
12346 10
12347 8
...
You don't need the table avg_marks.
Use window function AVG() in enrolments to get the average mark for each course_id and then use conditional aggregation with AVG() aggregate function to get the percentage:
SELECT course_id,
ROUND(100 * AVG((mark > avg_mark)::int)) percentage
FROM (SELECT *, AVG(mark) OVER (PARTITION BY course_id) avg_mark FROM enrolments) e
GROUP BY course_id
See the demo.
select
e.course_id,
count(case when e.mark > av.avg_mark then e.student end)/count(*) * 100 as students_cnt_with_mark_above_avg_pct
from enrolments e, avg_mark av
where e.course_id = av.course_id
group by e.course_id
I have a table like this
city metric_name metric_value id
Berlin likes 1 1a
Berlin dislikes 2 1a
Berlin comments 3 1a
Berlin likes 4 1b
Berlin dislikes 5 1b
Berlin comments 3 1b
Hamburg likes 1 1c
Hamburg dislikes 2 1c
Hamburg comments 3 1c
Hamburg likes 2 1d
Hamburg dislikes 4 1d
Hamburg comments 5 1d
and so on
My ideal result is this
city city_count_unique average_metric_score
Berlin 2 3 (sum metric_value / sum metric_names)
Hamburg 2 2,8
What I ve done
I got distinct count for every city and avg metric value
SELECT AVG(T.metric_value), T.city,
COUNT(*) AS 'city_count_unique'
FROM
(SELECT DISTINCT metric_value, city
FROM dbo.Table) as T
GROUP BY T.city
But it is false
Appreciate any help
updated
There is also an additional column id in varchar format
The answer here depends on this assumption:
You always have exactly 3 metrics per 'group' (i.e. likes, dislikes
and comments)
If that assumption is correct, the following will output what you are looking for:
SELECT city,
COUNT(metric_name) / 3 AS city_count_unique,
CAST(SUM(metric_value) AS FLOAT) / COUNT(metric_value) AS average_metric_score
FROM #Table
GROUP BY city
Output:
city city_count_unique average_metric_score
Berlin 2 3
Hamburg 2 2.83333333333333
How does this work?
By grouping on the city, we combine results for each city individually.
The count of metric_name gives the total metrics for that city (which is 6 in the case of your example). I divide this by 3 to give the unique count (based on the assumption I stated).
The average_metric_score calculation if the total of the metric_value for each city divided by the number of metrics (so 18 / 6 for Berlin). The reason for the CAST to FLOAT is to allow for a floating point answer. You could also use CONVERT if you prefer this to CAST.
Edit following OP update to question
OP edited the question to indicate that there is an ID column that allows the detection of metric grouping. This is an update to use that rather than assuming there are always 3 metrics per group.
SELECT city,
COUNT(id) AS city_count_unique,
CAST(SUM(metric_value_total) AS FLOAT) / SUM(metric_value_count) AS average_metric_score
FROM (
SELECT city,
id,
SUM(metric_value) metric_value_total,
COUNT(metric_value) AS metric_value_count
FROM #Table
GROUP BY city, id
) a
GROUP BY city
You seem to want:
SELECT city,
COUNT(DISTINCT id) as city_count_unique,
AVG(metric_value * 1.0) as average_metric_score
FROM t
GROUP BY city;
I have a table with 4 columns SID, Physics, Chemistry, Math. I need to get SID and maximum marks irrespective of the Subject, can anyone help me please.
For Eg
SID Physics Chemistry Maths
1 25 30 85
2 45 28 91
3 97 40 76
Output
SID Max_Marks
3 97
Most databases support least() and greatest(). So, you can do something like this:
select sid, greatest(Physics, Chemistry, Maths) as max_marks
from t
order by max_marks desc
limit 1;
This syntax is more suitable for MySQL. However, you can do something similar in almost any database.
course Table
course_code course_name credit_points_reqd
1 Comp Science 300
2 Soft Engineering 300
subject Table
subject_code subject_name credit_points
CS123 C Prog 15
CS124 COBOL 15
enrolment table
student_id student_name course_code subject_code Results
1 Lara Croft 1 CS123 70
1 Lara Croft 1 CS124 50
2 Tom Raider 2 CS123 60
2 Tom Raider 2 CS124 40
3 James Bond 1 CS123 NULL
3 James Bond 1 CS124 40
OUTPUT TABLE
student_name course_name credit_points_obt credit_points_reqd
Lara Croft Comp Science 30 300
Tom Raider Soft Engineering 15 300
I'm currently using TSQL. So here's the situation. I've prepared these tables to get the output like the way it i showed u up there. I need to calculate the credit points obtained. Credit points are achieved if the student receives > 50 for a subject they took. I want to ignore students that has not received any credit points at all (eg, James Bond is ignored as he has not achieved any points yet)
select student_name, course_name,credit_points_obt,credit_points_reqd
FROM enrolment (SELECT student_full_name, SUM(credit_points) AS credit_points_obt
FROM enrolment
GROUP BY student_id),
Totally stuck...I have no idea where to go now.
You can sum conditionally to get points for subject. If none are given result will be null, so you filter out those student/course pairs in having clause.
I've changed > 50 condition to >= 50 because your results contradict your requirements. Also, by the data I'd say that you have omitted student table for brewity, but if you haven't, it is a must.
Live test is # Sql Fiddle.
select enrolment.student_name,
course.course_name,
course.credit_points_reqd,
sum(case when enrolment.results >= 50
then subject.credit_points
end) credit_points_obt
FROM enrolment
inner join course
on enrolment.course_code = course.course_code
inner join subject
on enrolment.subject_code = subject.subject_code
group by enrolment.student_name,
course.course_name,
course.credit_points_reqd
having sum(case when enrolment.results >= 50
then subject.credit_points
end) is not null
I have a table like this:
Date StudentName Score
01.01.09 Alex 100
01.01.09 Tom 90
01.01.09 Sam 70
01.02.09 Alex 100
01.02.09 Tom 50
01.02.09 Sam 100
I need to rank the students in the result table by score within different dates, like this:
Date Student Rank
01.01.09 Alex 1
01.01.09 Tom 2
01.01.09 Sam 3
01.02.09 Alex 1
01.02.09 Sam 1
01.02.09 Tom 2
How can I do this in SQL?
You want to use the rank function in T-SQL:
select
date,
student,
rank() over (partition by date order by score desc) as rank
from
grades
order by
date, rank, student
The magic is in the over clause. See, it splits up those rankings by date, and then orders those subsets by score. Brilliant, eh?
You should use ORDER BY:
SELECT * FROM Students ORDER BY Date,Rank
That will order the data by date, then rank. You can add as many fields as you want, as long as they are comparable (you can't compare BLOBs or long text fields).
Hope that helps.
Do you know about using an ORDER BY clause?
You need to write a function that will computer the rank for a given student and date. Then you can "ORDER BY Date, Rank()"