Get the count of students who have passed all of their subjects - mysql

I want to get the amount of students who have passed ALL their subjects (mark >= 4).
Ex:
Student 1(OK):
Math: 5
Chemistry: 4
Student 2(OK):
Philosophy: 7
Student 3(NOT OK):
Math: 3
Philosophy: 6
Student 4(NOT OK):
Math: null
Philosophy: 8
DB:
-students(id)
-subjects_students(id_subject, id_student, mark)
SQL (using MySQL):
SELECT count(ss.id_student)
FROM subjects_students ss
WHERE (SELECT count(ss.id_student)
FROM students st
WHERE ss.id_student = st.id)
=
(SELECT count(ss.id_student)
FROM students st
WHERE ss.id_student = st.id
AND ss.mark >= 4)
I canĀ“t seem to get the right amount. I get students who have passed some subjects, but not all of them.
EDIT: mark can be null. Do not count these.
ANSWER:
SELECT COUNT(*)
FROM
(
SELECT COUNT(ss.id_student)
FROM subjects_students ss
GROUP BY ss.id_student
HAVING MIN(ss.mark) >= 4 AND COUNT(ss.mark) = COUNT(*)
) src;

If you want students who have passed all their subjects, then you want to filter out the ones whose mark is too low. Here is one method:
SELECT ss.id_student
FROM subjects_students ss
GROUP BY ss.id_student
HAVING MIN(ss.mark) >= 4;
You can then count the students using this as a subquery.
Note that the join to the students table is unnecessary. All the information you need is in subjects_students.

This should join the students and subject table, then remove any students who have a subject with a failing grade.
SELECT
a.`id`,
c.`id_subject`,
c.`mark`
FROM `students` a
LEFT JOIN `subjects_students` c
ON a.`id` = b.`id_student`
LEFT JOIN `subjects_students` b
ON a.`id` = b.`id_student` AND b.`mark` < 4
WHERE b.`id` IS NULL
GROUP BY a.`id`,c.`id_subject`,c.`mark`;

I do like Gordon's method here is another that might be slightly slower but can get you to the count of students in the same query without 2 steps.
SELECT COUNT(DISTINCT ss.id_student) as CountOfStudents
FROM
subjects_students ss
WHERE
NOT EXISTS (SELECT 1 FROM subjects_students ss2 WHERE ss.id_student = ss2.id_student AND ss2.mark < 4)

Related

MySQL, count total numbers of each IDs in two or more tables

I have 3 individual students (table_student), each student have attended 3 classes (table_class_1, table_class_2, table_class_3)
Image below are the list of student table (table_student), class 1 (table_class_1), class 2 (table_class_2) and class 3 (table_class_3) together with the total result of each of the class table.
My goal is to count how many times each students has entered each of the classes. So my expected result would be as shown as image below
What I can do so far was using union but the result is not what i expected to be
SELECT COUNT(B.student_id) AS count_class_1, NULL, NULL FROM table_student A
INNER JOIN table_class_1 B ON A.id = B.student_id
WHERE B.student_id = 1
UNION
SELECT NULL, COUNT(B.student_id) AS count_class_2, NULL FROM table_student A
INNER JOIN table_class_2 B ON A.id = B.student_id
WHERE B.student_id = 1
UNION
SELECT NULL, NULL, COUNT(B.student_id) AS count_class_3 FROM table_student A
INNER JOIN table_class_3 B ON A.id = B.student_id
WHERE B.student_id = 1
Your tables are not normalized correctly which is part of the reason you are facing this challenge. Have three tables as follows:
table_student (id, student_name),
table_class (id, class_name),
table_student_class(id, table_student_id, table_class_id)
Populate the data to the tables and to get the student attendance count use the following statement.
select table_student_id, table_class_id, Count(id) as class_count
from table_student_class
group by table_student_id, table_class_id
The results will be a class attendance count for each student.
I would recomend having one table for classes.
But what you whant is possible too:
SELECT
ts.id,
ts.student_name,
(SELECT COUNT(*) FROM table_class_1 tc1 WHERE tc1.student_id = ts.id) c1,
(SELECT COUNT(*) FROM table_class_2 tc2 WHERE tc2.student_id = ts.id) c2,
(SELECT COUNT(*) FROM table_class_3 tc3 WHERE tc3.student_id = ts.id) c3
FROM
table_student ts
ORDER BY
ts.student_name
;

Mysql row position(rank)

I have a mysql table "course_marks" which has 3 fields
studentID, courseID, mark.
I want to get the rank of a particular studentID for a particular courseID on the basis of mark(order by mark desc). The query should return a single row that indicates the rank. Ignore the same mark condition. How can I get the result in a single mysql query ?
First of all you should decide how identical marks will be treated when calculating the rank. This query for marks: 5,5,4 will calculate the rank of students with mark 4 as 3rd rank:
SELECT
COUNT(r.mark)+1 as rank
FROM
course_marks cm
LEFT JOIN
course_marks r ON r.courseID = cm.courseID AND r.mark > cm.mark
WHERE
cm.studentID = %STUDENT_ID% AND
cm.courseID = %COURSE_ID%
This query for marks: 5,5,4 will calculate the rank of students with mark 4 as 2nd:
SELECT
COUNT(DISTINCT r.mark)+1 as rank
FROM
course_marks cm
LEFT JOIN
course_marks r ON r.courseID = cm.courseID AND r.mark > cm.mark
WHERE
cm.studentID = %STUDENT_ID% AND
cm.courseID = %COURSE_ID%

Using ALL with MySQL

I have three tables: Suppliers (id, name...), TypeOfServices(id, service...) and SupplierService(supplierId, ServiceId).
As search input, I have a list with type of service ids. I can have (1) or (1,3) or (1,2,3). I need a query in MySQL that will return to me all the Suppliers that provide the services that are in the search (it can provide other services as well, but it MUST provide the ones that are in the search).
I can't use IN, because IN(2,3) will return the suppliers that have serviceId 2 OR 3, and I need 2 AND 3.
I tried with ALL like this:
SELECT * FROM suppliers as s
INNER JOIN supplierservices as ss ON ss.SupplierId = s.Id
WHERE ss.ServiceId = ALL (SELECT id FROM typeofservices WHERE id IN (2, 3))
but it's not giving me any results and I have a supplier that provides both services (with id 2 and 3). I think that SQLServer accepts the query like this ALL(2,3), but in MySQL it's not working like that, you have to have a query inside ALL.
Try
SELECT * FROM suppliers as s
INNER JOIN supplierservices as ss ON ss.SupplierId = s.Id
WHERE ss.ServiceId IN(2,3)
GROUP BY s.id
HAVING COUNT (DISTINCT ServiceId)=2
You can do this as:
SELECT s.*
FROM suppliers s INNER JOIN
supplierservices ss
ON ss.SupplierId = s.Id
GROUP BY s.id
HAVING sum(id = 2) > 0 and
sum(id = 3) > 0;
Just add a new condition to the having clause for each value you need.

Get list of students with max number in each class

I want to get the list of students from db, who have get maximum number in each class with student name. Using MySQL DB.
I have the following tables like Student, classes, result (with different years results)
Tables structure student(student_id,student_name,class_id,address), class(class_id,class_name), results(result_id,student_id,year,marks)
and I need list like
Student Name class Marks
Jon A-1 800
Steve B-1 789
Edit corrected code, comment was correct
Try the code on this SQL Fiddle link
You could use a subquery to filter the students with the highest mark per class:
select s.student_name
, c.class_name
, r.marks
from results r
join student s
on r.student_id = s.student_id
join class c
on c.class_id = s.class_id
where r.result_id =
(
select r2.result_id
from student s2
join results r2
on s2.student_id = r2.student_id
where c.class_id = s2.class_id
order by
r2.marks desc
limit 1
)
Live example at SQL Fiddle.
select s1.student_name, c1.class_name, r1.marks
from student s1, class c1, results r1,
(select s2.class_id, max(r2.marks) marks
from results r2, student s2
where r2.student_id = s2.student_id
group by s2.class_id) agg
where r1.marks = agg.marks
and r1.student_id = s1.student_id
and s1.class_id = c1.class_id
and s1.class_id = agg.class_id

MySQL SELECT DISTINCT ORDER BY problem

To begin with I have 4 tables I am dealing with.
I have a classes table that is a 1->N relationship with a sections table which also has a 1->N relationship with a lessons table.
So to put it in perpective:
Classes
Sections
Lessons
The last table is an activityLog, when the student accesses a lesson this is recorded using the following:
ActivityLog Row -> actorID (user ID), classID, sectionID, lessonID
I want to pull out the last 5 unique lessons the student has visited. I tried using both DISTINCT and GROUP BY without success.
The same records are being returned each time, not the latest classes that they have visited.
Using GROUP BY
SELECT activityLog.actorID, activityLog.activityDate,
strClasses.classID, strClasses.className,
strSections.sectionID, strSections.sectionName,
strLessons.lessonID, strLessons.lessonName
FROM activityLog
LEFT JOIN strClasses ON strClasses.classID = activityLog.classID
LEFT JOIN strSections ON strSections.sectionID = activityLog.sectionID
LEFT JOIN strLessons ON strLessons.lessonID = activityLog.lessonID
WHERE activityLog.activityTypeID = 6 AND activityLog.actorID = 3
GROUP BY activityLog.lessonID
ORDER BY activityLog.activityDate DESC
LIMIT 5
Using DISTINCT
SELECT DISTINCT activityLog.actorID,
strClasses.classID, strClasses.className,
strSections.sectionID, strSections.sectionName,
strLessons.lessonID, strLessons.lessonName
FROM activityLog
LEFT JOIN strClasses ON strClasses.classID = activityLog.classID
LEFT JOIN strSections ON strSections.sectionID = activityLog.sectionID
LEFT JOIN strLessons ON strLessons.lessonID = activityLog.lessonID
WHERE activityLog.activityTypeID = 6 AND activityLog.actorID = 3
ORDER BY activityLog.activityDate DESC
LIMIT 5
I cannot figure out why the latest records are not being displayed.
Based on your change, how does this suit you?
SELECT activityLog.actorID, activityLog.activityDate,
strClasses.classID, strClasses.className,
strSections.sectionID, strSections.sectionName,
strLessons.lessonID, strLessons.lessonName
FROM activityLog
LEFT JOIN strClasses ON strClasses.classID = activityLog.classID
LEFT JOIN strSections ON strSections.sectionID = activityLog.sectionID
LEFT JOIN strLessons ON strLessons.lessonID = activityLog.lessonID
WHERE activityLog.activityTypeID = 6 AND activityLog.actorID = 3
AND activityLog.activityDate = (SELECT MAX(activityDate) FROM activityLog AS lookup WHERE lessonID = activityLog.lessonID)
ORDER BY activityLog.activityDate DESC
LIMIT 5
Based on your description, I'm not sure why you're using LEFT JOIN, but I've left it in just in case.
Try group by like below
GROUP BY activityLog.classID,activityLog.sectionID,activityLog.lessonID
I think it will work, or just sent me create scripts for these I will create that query
Well, there's got to be a datetime in the ActivityLog I hope... so Try this:
Select s.Name, c.ClassName
From Students s
left Join On Classes c
On c.ClassId In
(Select Distinct ClassId From Classes
Where (Select Count(Distinct ClassId) From Classes ic
Join ActivityLog l On l.UserId = s.UserId
And l.ClassId = c.ClassId
Where classId = c.ClassId
And activityDateTime > l.activityDateTime)
< 5)