How to select only ranks which occur once except the first rank? - mysql

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

Related

Highest total salary

I have 3 tables with the following values and the question is : Find the name of the instructor with the highest total salary along with the names of the dancers he trains.
I was thinking something like :
Select Instructor.name, Dancer.name
FROM Instructor, Dancer, Info
WHERE Instructor.i_ID=Info.ints_id
AND Dancer.d_ID=Info.danc_id
GROUP BY Info.inst_id, Info.danc_id
ORDER BY SUM(Info.salary)
DESC LIMIT 2;
But the output is not right and also I am not suppose to know how many dancers every instructor has, so DESC LIMIT 2 is definitely wrong.
Join properly the tables and group by instructor to get the total salary for each.
Then sort descending by the total salary and keep only the 1st row.
With group_concat() you get the names of the dancers as a comma separated list:
select i.name,
sum(n.salary) total_salary,
group_concat(d.name) dancers
from instructor i
inner join info n on n.inst_id = i.i_id
inner join dancer d on d.danc_id = d.d_id
group by i.i_id, i.name
order by total_salary desc limit 1
Note that this query will return only 1 row, so if there are 2 instructors with the same top total salary you will get only 1.
If you want in the results ties in the top salary then use a HAVING clause:
select i.name,
sum(n.salary) total_salary,
group_concat(d.name) dancers
from instructor i
inner join info n on n.inst_id = i.i_id
inner join dancer d on d.danc_id = d.d_id
group by i.i_id, i.name
having total_salary = (select sum(salary) from info group by inst_id order by sum(salary) desc limit 1)
Try This:
select tab3.name "Instructor",tab2.name "Dancer"
from info tab1
inner join
dancer tab2 on tab1.dance_id=tab2.d_id
inner join
(select t1.inst_id,t2.name, sum(t1.salary) as "total_salary" from info t1
inner join instructor t2
on t1.inst_id=t2.i_id
group by t1.inst_id,t2.name
order by total_salary desc limit 1) tab3
on tab3.inst_id=tab1.inst_id
DB-FIDDLE
well it's a bit odd relation table but you could try something like this
SELECT
ins.name,
SUBSTRING_INDEX(GROUP_CONCAT(inf.salary ORDER BY inf.salary DESC),',',1) salary,
SUBSTRING_INDEX(GROUP_CONCAT(d.name ORDER BY inf.salary DESC),',',1) dancer_name
FROM Instructor ins
JOIN Info inf ON (ins.i_ID=inf.inst_id)
JOIN Dancer d on (d.d_ID=danc_id)
GROUP BY ins.i_ID
LIMIT 1;

Creating a ranking column from a queried column in MySQL

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;

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

Count rows with bigger average

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

SQL query to find out third highest salary involving two tables

I have two tables:
Persons:
empid(primary key)
firstname
lastname
email
Details:
Did(primary key)
salary
designation
empid
Now I want to select firstname, lastname, salary of the employee with the 3rd highest salary.
KEEP IT SIMPLE SINCE I AM A BEGINNER.
Also it would be great if you can suggest me a site where I can find SQL query exercises with solutions (I do not need an online interpreter, I have SQL Server 2008 to practice on ), I have completed w3schools (which was awesome) but I need exercises now, I tried sql.ex.ru but it was not that helpful.
try this:
SELECT top 1 P.*,a.salary
FROM Persons p
JOIN
(select top 3 empid,salary from Details order by salary desc)a
ON p.empid=a.empid
ORDER BY p.salary
or
;WITH CTE AS
(SELECT *,
ROW_NUMBER() OVER (ORDER BY SALARY DESC) AS ROW_NUM
FROM DETAILS)
SELECT *
FROM PERSONS P
JOIN CTE C
ON P.EMPID=C.EMPID
where c.ROW_NUM=3
Here is a little trick. What it does is add a sequence number or row number onto each row, giving the ordering by salary. It then just chooses the third one:
select FirstName, LastName, Salary
from (select p.FirstName, p.LastName, d.salary,
row_number() over (order by salary desc) as seqnum
from persons p join
details d
on p.empid = d.empid
) t
where seqnum = 3
The row_number() function does this calculation. It is a very useful function, that you can add to your SQL knowledge.
SELECT TOP 1 persons.FirstName, persons.LastName, Salary
FROM persons inner join Details
ON persons.empid=Details.empid
where Salary not in(SELECT TOP 2 Salary from Details ORDER BY Salary DESC)
order by Salary desc