Calculate percentage of students SQL - sql

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

Related

How to select province and average weight of people in those provinces only for provinces which average weight of people < 70

A table named "people" contains the following fields:
first_name
weight
gender
province
A
85
Male
Lubusz
B
95
Female
Lubusz
C
90
Female
Silesia
D
95
Male
Silesia
E
40
Male
Opole
F
50
Female
Opole
How to select the name of province and average weight of people in those provinces, only for provinces which the average weight of people is < 70.
Expected output:
province
Avg_Weight
Opole
45
I have tried the following code:
SELECT province, AVG(weight) AS "Avg_Weight"
FROM people
WHERE "Avg_Weight" < 70 GROUP BY province;
However, the result returns the overall average weight of people in every province.
You need to use HAVING clause & not WHERE clause as fiter is to be done on aggregated column.
select
"province",
avg("weight") as avg_weight
from
people
group by
"province"
having
avg("weight") < 70;
Demo

SQL Server How to get number of unique values in a column and average score per values?

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;

SQL counting with condition

If I have a table called Buildings.
Room_No Bldg Capacity
112 SCEN 23
242 JBHT 25
542 SCEN 4
324 JBHT 24
What I want is to print out the Bldg name and the total number of rooms that have a capacity more than 20 in each building. So it is supposed to look like:
Bldg Total
SCEN 1
JBHT 2
Am I going on the right track:
Select Bldg, Count(Capacity > 20) as Total from Buildings Group By Total Desc
You could use CASE:
Select Bldg, Count(CASE WHEN Capacity > 20 THEN 1 END) as Total
from Buildings
Group By Bldg
ORDER BY Total DESC;
If you are using Postgresql you could rewrite it as:
Select Bldg, Count(1) FILTER(WHERE Capacity>20) as Total
from Buildings
Group By Bldg
ORDER BY Total DESC;
Rextester Demo
The other answers seem overly complicated for this problem. The solution is rather simple:
SELECT Bldg, COUNT(*) AS count
FROM Buildings
WHERE Capacity > 20
GROUP BY Bldg
Here is the Fiddle: http://sqlfiddle.com/#!9/308a6/1

How to select students who got above average?

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.

How to get the student academic progress?

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