Count rows with bigger average - mysql

Each student has some scores (and each score has student_id column).
I want to calculate student average, compare his average with other student, and find his position in his class.
Is it possible to find his position based on his average with 1 query? (may contains subqueries or joins)
I can sort all students by their average by this query:
SELECT s.*
FROM
scores s LEFT JOIN lessons lesson
ON lesson.id = s.lesson_id
WHERE lesson.display = 1
GROUP BY s.student_id
ORDER BY AVG(s.score) DESC
but it needs processing with PHP array_search function. (I think using MySQL functions is better, in this situation)

select student_id, AVG(scores) as 'average' from lessons as l, scores as s
where lessons.id = s.lesson_id and lesson.display = 1
GROUP BY s.student_id order by average desc`
Try this query
sample http://sqlfiddle.com/#!2/4fb8d/1
Hope this helps

select s.id, AVG(l.scores) as average from lessons as l, student as s
where l.id = s.lessonid and l.display = 1
GROUP BY s.id order by average desc
can solve your problem
Check this link
Thanks to Meherzad's sql query

I think you are looking for something like this:
SELECT COUNT(DISTINCT average)
FROM (
SELECT AVG(score) as average
FROM scores INNER JOIN lessons
ON scores.lesson_id = lessons.id
WHERE display=1
GROUP BY student_id
) sub_scores
WHERE average >= (SELECT AVG(score)
FROM scores INNER JOIN lessons
ON scores.lesson_id = lessons.id
WHERE display=1
AND student_id=1)
In the subquery sub_scores I'm calculating all averages of all students, in the outer query I'm calculating the number of distinct averages bigger than the average of student 1.
This will return the position of student 1.
Depending on what you are after, you might want to remove DISTINCT clause.
See this fiddle.

You need to use a user defined variable over the result set.
Try this:
SELECT *, (#rank := if(#rank is null, 1, #rank + 1)) as rank
FROM (SELECT s.*
FROM scores s
LEFT JOIN lessons lesson ON lesson.id = s.lesson_id
WHERE lesson.display = 1
GROUP BY s.student_id
ORDER BY AVG(s.score) DESC
) x

Related

How to use MAX and COUNT function in MYSQL with 2 tables

I am a newbie in MYSQL and had a question regarding the use of MAX and COUNT functions together in MYSQL. I have 2 tables worker and assignment and the primary key of worker is a foreign key in assignment table.
I need to show the employees name and id and the total assignment assigned to him, and only show the person with the most assignment that is the employee with the most assignment.
my code is
SELECT worker.Wrk_ID, worker.Wrk_LastName, MAX(a.count_id)
FROM worker,
(SELECT COUNT(assignment.Wrk_ID) as count_ID
FROM worker, assignment
WHERE worker.Wrk_ID = assignment.Wrk_ID
GROUP BY worker.Wrk_ID)as a
GROUP BY worker.Wrk_ID;
The code is giving an error no. #1054.
Please can anyone help me.
Thanking you in anticipation.
Try something like this:
SELECT worker.Wrk_ID, worker.Wrk_LastName, S.Count
FROM worker
JOIN
(SELECT Wrk_ID, COUNT(*) AS Count FROM Assignments
GROUP BY Wrk_Id ORDER BY COUNT(*) DESC LIMIT 1) S
ON worker.Wrk_ID = S.Wrk_ID
If you want a list of employees sorted by their total assignments:
SELECT w.WrkID, w.Wrk_LastName, COUNT(*) AS Assignments
FROM work w left join Assignments a
ON w.WrkID=a.WrkID
GROUP BY w.WrkID
ORDER BY COUNT(*) DESC;
To allow multiple winners:
SELECT s.*, w.Wrk_Lastname FROM
(
SELECT wrk_id , COUNT(*) AS tot_assignments
FROM Assignments
GROUP BY wrk_id
HAVING COUNT(*) =
(
SELECT MAX(tot) FROM
(
SELECT COUNT(*) AS TOT FROM Assignments GROUP BY wrk_id
) counts
)
) winners
INNER JOIN worker w ON s.wrk_id = w.wrk_id;
It can be slow since it does multiple GROUP BY. Doing it in separated steps in a procedure can be better.

SQL join tables and get Average

I asked yesterday a little bit similar question (I thought that that was my problem but later i realised that there was a fault). But that question got couple of nice answers and it did not make sense to change that question. And i think this question is enough different.
Question:
I have four tables and i need to calculate the Average points that each School has gotten.
Problem: the School Average should be calculated by the two latest Points each Team has gotten. At the moment the Query calculates all the points a Teams has gotten in the average.
A School can have multiple Teams and Teams can have multiple points. And from each team only the two latest points should be calculated in the School Average. Each School should also get the proper City KAID (CITY_ID). In the sqlFiddle everything works but the Average is wrong because it calculates all the points a Team has gotten.
I have created a simplificated working: sqlFiddle
The average for SCHOOL1 should be 2,66...
Example:
Let's say that Team10 has 6 points:
TEAM10 3..4..7..0..3..5 = 8 (3+5=8)
Only the latest two points should be calculated in the average (3 and 5). This should happen for all the teams.
I have tried couple of Queries but they don't work.
Query 1 (Problem: calculates all the points):
SELECT SNAME As School, AVG(PTS) As Points, ka.KAID As City_id FROM
Schools op
LEFT JOIN Points pi
ON op.OPID = pi.OPID
LEFT JOIN Citys ka
ON op.KAID = ka.KAID
GROUP BY SNAME, ka.KAID
ORDER BY City_id, Points, School ASC
Query 2 (Problem: Average wrong and duplicates):
SELECT IFNULL(AVG(PTS), 0) AS AVG, po2.KAID AS KID, SNAME AS SNAM FROM
(
SELECT te1.ID, te1.KAID, po1.PTS, te1.OPID FROM Points po1
INNER JOIN Teams te1 ON te1.ID = po1.TEID
GROUP BY po1.TEID, te1.ID HAVING count(*) >= 2
)
po2 INNER JOIN Schools sch1 ON po2.KAID = sch1.KAID
GROUP BY sch1.SNAME, sch1.OPID
ORDER BY po2.ID DESC
I am quite new to sql I have tried different Queries but i haven't gotten this to work properly.
If something is not clear please ask i will try to Explain it better.
try running this...
SELECT
SNAME As School,
SUM(pts)/ count(*) As Points,
ka.KAID As City_id
FROM Schools op
LEFT JOIN Points pi
ON op.OPID = pi.OPID
LEFT JOIN Citys ka
ON op.KAID = ka.KAID
GROUP BY SNAME, ka.KAID
ORDER BY City_id, Points, School ASC
DEMO
From what I see you have for the first school and the first city 8 rows with the sum = 29.
29/8 = 3.25.. you are joining the tables on the correct fields and the query is returning the rows in the table based on the opid and kaid so it seems the results are correct.. i'm guessing the avg function is not including the 0's or something but the results are there
EDIT:
to get it for the two newest rows you need to look at the greatest id per school and then the second greatest.. this will do what you want.
SELECT
SNAME As School,
SUM(pts)/ count(*) As Points,
ka.KAID As City_id
FROM Schools op
LEFT JOIN Points pi ON op.OPID = pi.OPID
LEFT JOIN Citys ka ON op.KAID = ka.KAID
JOIN
( ( SELECT MAX(id) as f_id
FROM points
GROUP BY TEID
ORDER BY f_id
)
UNION
( SELECT p1.id
FROM
( SELECT MAX(id) as t_id
FROM points
GROUP BY TEID
ORDER BY t_id
)t
LEFT JOIN points p1 on p1.id = (t.t_id -1)
)
) temp ON temp.f_id = pi.id
GROUP BY SNAME, ka.KAID
ORDER BY City_id, Points, School ASC;
ANOTHER DEMO

Show MAX value with a join

I'm struggling a little with a query I'm trying to build. For a game I want to display the top 10 of scores.
My table looks like this:
player = id, playername, username
score = id, track, car, bestscore, totalscore, player_id (foreign key to player.id)
For the top10 I want to show the playername, the total score, the car and the track.
The current query I have is:
SELECT p.playername, MAX(s.totalscore) totalscore, s.car, s.track
FROM player p
INNER JOIN score s on p.id = s.player_id
GROUP BY p.playername
ORDER BY MAX(s.totalscore) DESC
LIMIT 10
This seems to work fine, except for one problem. If a user has a score of 50 on track 1 with car 1, and then puts a score of 60 on track 1 with car 2, I see car 1 on the query. It does not seem to get the according car of the top score if this user.
I hope it makes sense what I just told.
Thanks in advance!
EDIT:
SELECT p.playername, MAX(s.totalscore) as totalscore, s.car, s.track
FROM (SELECT * FROM score ORDER BY totalscore DESC) s
INNER JOIN player p ON s.player_id = p.id
GROUP BY p.playername
ORDER BY MAX(s.totalscore) DESC
LIMIT 10
This query seems to do the trick. I've been trying around and the result-set is exactly what I mean. Is it any good though? I'm not good at SQL and just because it works doesn't always mean it's good, does it?
When you have a non-aggregated field as part of an aggregated query that isn't in your group by, and has a many to one relationship with the relation on which you are grouping, MySQL gives no guarantee what it will return.
This query would throw an error in another RDBMS. If you care about the top score by (person,car) tuple, just add it to the group by. However, if you want each player to have a max score, regardless of what car was used, you'll need a subquery or a self join.
One way to do it:
SELECT p.playername, s1.totalscore , s1.car, s1.track
FROM player p
INNER JOIN
score s1 on p.id = s1.player_id
WHERE s1.totalscore = ( SELECT MAX(totalscore) FROM score s2 WHERE s2.player_id=s1.player_id)
ORDER BY s1.totalscore DESC
LIMIT 10
You use "Group By" on playername so the other columns will group in "what mysql desides" manner. If you want to get the cars you should add - group by car.
SELECT p.playername, MAX(s.totalscore) totalscore, s.car, s.track
FROM player p
INNER JOIN score s on p.id = s.player_id
GROUP BY p.playername, s.car //added car
ORDER BY s.totalscore DESC
LIMIT 10

Calculating the AVG of a COUNT() in mysql

I am trying to issue the following query:
SELECT
AVG(`count`)
FROM
(SELECT
COUNT(s_id) AS `count`
FROM
instructor,
advisor
WHERE instructor.ID = advisor.i_id
AND dept_name = 'CIS'
GROUP BY dept_name) nested ;
to calculate the average of a counted value. However, the nested query returns to me a single row with the counted value = 2. This value 2 is a result of 2 rows of records that match the query.
Now when I apply the average function to it I get 2.0000. However the expected answer is 1.0000.
Any idea how I can get around to getting the expected value?
This is your query:
SELECT AVG(`count`)
FROM (SELECT COUNT(s_id) AS `count`
FROM instructor join
advisor
on instructor.ID = advisor.i_id
WHERE dept_name = 'CIS'
GROUP BY dept_name
) nested
You are selecting one department name and then grouping by that. The where clause means that you are only getting one row. So, the average is the same as the count. The average of one thing is the value of the thing.
If you want the average across all departments, you can remove the where:
SELECT AVG(`count`)
FROM (SELECT COUNT(s_id) AS `count`
FROM instructor join
advisor
on instructor.ID = advisor.i_id
GROUP BY dept_name
) nested;
Or, you don't really need the subquery:
SELECT count(*) / count(distinct dept_name)
FROM instructor join
advisor
on instructor.ID = advisor.i_id ;
AVG(s_id) will calculate the average s_id so if the two rows has the id's of 1 and 3 it will result as 2.

Assign alias to a table in subquery MySQL

I have two tables:
the exam table which contains the score performed by students
and the student table
Both are joined this way: student.id=exam.student_id.
I am trying to get the firt five students who have the highest score calculated by the average of their score over 5 days using the query below:
SELECT
student.id as std_id,
student.name,
(SELECT AVG(score) FROM exam WHERE exam.student_id=std_id ORDER BY exam.timestamp DESC LIMIT 5) AS score
FROM student
ORDER BY score
DESC LIMIT 5
I am having the following error:
#1054 - Unknown column 'std_id' in 'where clause'
I also tried it by replacing std_id by student.id but still no luck.
Any idea how to solve this issue? Great thanks
------------------------------------------------------------------------------
Sorry I made a mistake in my logic.
As said earlier on, the average is calculated for
only the last 5 scores recorded
.
Updated query:
SELECT
student.id as std_id,
student.name,
(SELECT AVG(score) FROM (SELECT score FROM exam WHERE exam.student_id=student.id ORDER BY exam.timestamp DESC LIMIT 5) AS score) AS score
FROM student
ORDER BY score
DESC LIMIT 5
The place which is giving the error is where I set exam.student_id=student.id
Thanks.
Just replace std_id with student.id - the alias doesn't exist yet during the subquery compilation since the outer query cannot be compiled until the subquery is considered valid (and its output known).
Your code looks like it should work to me (at least once the mod suggested by Niels Keurentjes has been done).
You could possibly use a generated sequence though rather than a correlated sub select. Something like thi:-
SELECT
student.id,
student.name,
AVG(Sub3.score)
FROM student
LEFT OUTER JOIN (
SELECT Sub1.student_id, Sub1.score, #aSeq := IF(#PrevStudent = student_id, #aSeq + 1, 0) AS Sequence, #PrevStudent := student_id
FROM (SELECT * FROM exam ORDER BY student_id, timestamp DESC) Sub1
CROSS JOIN (SELECT #PrevStudent := 0, #aSeq := 0) Sub2
) Sub3
ON student.id = Sub3.student_id
WHERE (Sub3.Sequence IS NULL OR Sub3.Sequence < 5)
GROUP BY student.id, student.name