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
Related
I have 2 tables
Table1: student
Table2: problem
Now we need to find out which student has solved how many problems and in the output we need to display student_id, student_name and no_of_prob in descending order of no_of_prob.
If more than one student has equal number of problems then 1. If the no_of_prob is highest among all others then keep all of them. 2. If the no_of_prob is not the highest, then do not keep any of these.
For example, when we group by student_id, we get this
As student_id 2 and 5 have equal no_of_prob but as their no_of_prob is highest, we will keep both student_id 2 and 5. But the student_id 1 and 3 have equal no_of_prob but its not the highest therefore we need to drop both of these. The final table should be like this.
Now i have found out how to get till the table number 3 above but I don't know how am I supposed to keep the 1st rankers but remove lower rankers if repeated. This is my code below
select s.student_id,student_name, count(problem_id) as no_of_prob,
rank() over(order by count(problem_id) desc) as st_rank
from student s inner join problem p on s.student_id = p.student_id
group by s.student_id;
You can use the RANK() function to get the 1st rankers along with rows having count as 1 with the subquery.
SELECT student_id,
student_name,
no_of_prob
FROM (SELECT p.student_id,student_name,
COUNT(*) AS no_of_prob,
RANK() OVER(ORDER BY COUNT(*) DESC) rk
FROM problem p
JOIN student s ON p.student_id = s.student_id
GROUP BY p.student_id,student_name
ORDER BY COUNT(*) DESC) a
WHERE rk = 1 OR no_of_prob = 1
Check Demo Here
Output
Try this query. Hopefully it will be work fine.
SELECT p.student_id,s.student_name,count(*) as no_of_prb
FROM `problem` p
left join student s on p.student_id=s.student_id
group by p.`student_id`
having
no_of_prb not in (select count(*) as total from problem group by student_id having total>1)
OR
no_of_prb = (select count(*) as total from problem group by student_id order by total desc limit 1)
order by no_of_prb desc
I want to build a ranking column into my query--I've found some similar cases on Stack but this one's a little different and I can't quite make it work. I have a single table, EnrollmentX, with two columns, a unique StudentID and a GroupId (for sake of argument, groups 1:3). I need to simultaneously count the number of students in each of these three groups and then rank the the groups by number of students. I've made it as far as the counting:
SELECT
EnrollmentX.GroupId,
COUNT(EnrollmentX.StudentId) AS StudentCnt
FROM EnrollmentX
GROUP BY
EnrollmentX.GroupId
This puts out two columns, one for GroupId, 1:3, and one for StudentCnt, with the correct number of students in each group. What I can't work out is how to use that StudentCnt column after building it to create a third ranking column.
IF you are on mysql 8 there are more readable options.Change the order in the inner query if you want a different rank.
SELECT GroupId, StudentCnt, #Rank:=#Rank + 1 AS rank FROM
(SELECT EnrollmentX.GroupId,
COUNT(EnrollmentX.StudentId) AS StudentCnt
FROM EnrollmentX
GROUP BY
EnrollmentX.GroupId
ORDER BY StudentCnt DESC
) x CROSS JOIN (SELECT #Rank:=0) y
Try this query:
select ex.GroupId, ex.StudentId, exg.cnt from EnrollmentX ex
left join (
SELECT GroupId, COUNT(*) cnt
FROM EnrollmentX
GROUP BY GroupId
) exg on ex.GroupId = exg.GroupId
order by exg.cnt
try it..
SET #Rank = 0;
SELECT #Rank:=#Rank + 1 rank, EnrollmentX.GroupId,
COUNT(EnrollmentX.StudentId) StudentCnt
FROM EnrollmentX
GROUP BY
EnrollmentX.GroupId
ORDER BY StudentCnt DESC;
I have read many posts have a solution for this but this does not work in my case. What am I doing wrong?
- This gives me the SUM of scores for every user and this is the first part.( Aggregated Data)
The Query result
SELECT user_id, sum(score) as total_user_score
FROM (
SELECT comments_proper.user_id, comments_proper.score
FROM assignment_2.comments_proper
) AS rsch
GROUP BY user_id;
However, I want only 2 records which contain the min and the max score values.
What am I doing wrong?
Oh dear, where to begin.
I have read many posts
You should have been paying attention to which ones got up-voted and good answers, and which were down-voted/closed. The former would have included the table structures, examples of input and expected output. And unambiguous questions.
I want only 2 records
Is that from the source data set or from the aggregated data set?
The latter is a slightly tricky problem which has been asked and answered many times here on SO, there are multiple solutions with different performance characteristics. There's even a chapter in the manual covering just this question. The current content at that link uses subqueries to identify the min/max value which replaces an earlier version of the documentation which explained the max-concat trick, but its also possible to use variables to identify the right caddidate rows in a sub-query or to use sorting.
However the SQL you've shown us here, has very little to do with solving the problem you describe, and is very badly written.
I won't provide examples of every solution, but this will solve your problem...
SELECT user_id, SUM(score)
FROM assignment_2.comments_proper
GROUP BY user_id
ORDER BY SUM(score)
UNION
SELECT user_id, SUM(score)
FROM assignment_2.comments_proper
GROUP BY user_id
ORDER BY SUM(score)
updated
I hadn't tested the above. I did test this:
SELECT *
FROM (
SELECT user_id, SUM(score)
FROM assignment_2.comments_proper
GROUP BY user_id
ORDER BY SUM(score) LIMIT 0,1
) as lowest
UNION ALL
SELECT *
FROM (
SELECT user_id, SUM(score)
FROM assignment_2.comments_proper
GROUP BY user_id
ORDER BY SUM(score) DESC LIMIT 0,1
) as highest
In your queries you have some problem of sintax and a too complex calculation for aggregated resul anyway .. in cp1.* result you have the min related values in cp2.* the max related.
If you need all the resuly for min and max rows on the same row you can use a couple of inner join based on the aggregated result
select cp1.* , cp2.*
from ( SELECT cp.user_id, sum(cp.score), min(cp.score) min_score, max(cp.score) max_score
FROM assignment_2.comments_proper cp
group by cp.user_id ) t
inner join assignment_2.comments_proper cp1 on cp1.user_id = t.user_id
and cp1.score = t.min_score
inner join assignment_2.comments_proper cp2 on cp2.user_id = t.user_id
and cp2.score = t.max_score
otherwise if you want the result in two rows one for min and one for max
select 'min' , cp1.*
from ( SELECT cp.user_id, sum(cp.score), min(cp.score) min_score, max(cp.score) max_score
FROM assignment_2.comments_proper cp
group by cp.user_id ) t
inner join assignment_2.comments_proper cp1 on cp1.user_id = t.user_id
and cp1.score = t.min_score
union
select 'max' , cp2.*
from ( SELECT cp.user_id, sum(cp.score), min(cp.score) min_score, max(cp.score) max_score
FROM assignment_2.comments_proper cp
group by cp.user_id ) t
inner join assignment_2.comments_proper cp2 on cp2.user_id = t.user_id
and cp2.score = t.max_score
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.
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