Get attempts of exams for every course of every student - mysql

I'm new to SQL. I want to get attempts of exams for every course of every student.
I've made this query in my database:
SELECT students.id, students.surname, students.name, courses.name AS "course" FROM `students`
JOIN exam_student
ON exam_student.student_id = students.id
JOIN exams
ON exam_student.exam_id = exams.id
JOIN courses
ON exams.course_id = courses.id
ORDER BY students.surname ASC, students.name ASC;
The query produces this
Now, what I want is to get for every user, the attempts of exams of every course name.
I have the exams pivot with user_id and course_id.
How can I reach this result?

Seems you want a total.
So let's COUNT the attempts by aggregating it.
SELECT s.id, s.surname, s.name
, c.name AS course
, COUNT(e.id) AS exam_attempts
FROM students s
LEFT JOIN exam_student es ON es.student_id = s.id
LEFT JOIN exams e ON e.id = es.exam_id
LEFT JOIN courses c ON c.id = e.course_id
GROUP BY s.id, s.surname, s.name, c.id, c.name
ORDER BY s.surname, s.name, c.name;

Related

Is this the right way to join tables to fetch data?

I have a database with the tables:
Student(SID,Name,Surname,Age)
Registration(StudentID,CourseID)
Course(CID,Name,Cost)
I would like to extract only the name of the courses with students younger than 20. Will the query below do just that?
SELECT C.NAME
FROM Course C
INNER JOIN Registration
INNER JOIN Student S
WHERE CID = CourseID
AND SID = StudentID
AND Age < 20
GROUP BY C.NAME
I would also like to extract the number of students in each course having students younger than 20. Is it correct to do it as below?
SELECT count(S.NAME)
,C.NAME
FROM Student S
INNER JOIN Course C
INNER JOIN Registration
WHERE Age < 20
AND CID = CourseID
AND SID = StudentID
GROUP BY C.NAME
You are missing the ON part for the join otherwise it would just be a CROSS JOIN.
Your first query should look like this if you want just a distinct list of student names:
SELECT DISTINCT C.NAME
FROM Course C
INNER JOIN Registration R ON C.CID = R.CourseID
INNER JOIN Student S ON R.StudentID = S.SID
WHERE Age < 20
Your second query shouldn't really have the C.Name in the select if you want to get just a count unless you want a count of how many students have that name.
SELECT count(*)
FROM Student S
INNER JOIN Registration R ON s.SID = R.StudentID
INNER JOIN Course C ON c.CID = R.CourseID
WHERE Age < 20
GROUP BY C.NAME
First join these tables, then group by Course's PK(CID), Add the HAVING condition to filter the course which has students younger than 20.
Then use Course table to join the result to get the course name and count of students in the course.
SELECT
T1.Name,
T2.StudentCount
FROM
Course T1
INNER JOIN (
SELECT
c.CID,
COUNT(s.SID) AS StudentCount
FROM
Course c
LEFT JOIN Registration r ON c.CID = r.CourseID
LEFT JOIN Student s ON s.SID = r.StudentID
GROUP BY c.CID
HAVING COUNT(IF(s.Age < 20, 1, NULL)) > 0
) T2 ON T1.CID = T2.CID
More correctly, you should move the conditions of the join, to the join statements themselves by including them in the on clause instead of the where. While the results may not change in this instance, if you were to start including outer joins you would encounter difficulties.
SELECT count(S.NAME)
,C.NAME
FROM Student S
INNER JOIN Registration R
ON s.SID = R.StudentID
INNER JOIN Course C
ON c.CID = R.CourseID
WHERE Age < 20
GROUP BY C.NAME
There's a fiddle here showing it in action: http://sqlfiddle.com/#!9/c3b8f/1
Your first query will also produce the results you want, but again, you should move the join predicates to the join itself. Also, you don't need to perform the grouping just to get distinct values, mysql has an expression for that called distinct. So rewritten, the first query would look like:
SELECT DISTINCT C.NAME
FROM Student S
INNER JOIN Registration R
ON s.SID = R.StudentID
INNER JOIN Course C
ON c.CID = R.CourseID
WHERE Age < 20.
Again, the results are the same as what you have already but it is easier to 'read' and will put you in good stead when you move on to other queries. As it stands you have mixed implicit and explicit join syntax.
This fiddle demonstrates both queries: http://sqlfiddle.com/#!9/c3b8f/4
edit
I may have misinterpreted your original question - if you want the total number of students enrolled in a course with at least one student under 19, you can use a query like this:
select name, count(*)
from course c
inner join registration r
on c.cid = r.courseid
where exists (
select 1
from course cc
inner join registration r
on cc.cid = r.courseid
inner join student s
on s.sid = r.studentid
where cc.cid = c.cid
group by cc.cid
having min(s.age) < 20
)
group by name;
Again with the updated fiddle here: http://sqlfiddle.com/#!9/c3b8f/17

SQL view all courses for one student

I have 3 tables
table_student
ID, firstname, lastname
table_courses
ID, course_name
table_student_course
ID, student_ID, course_ID, date_taken
All I want to do is list all courses by course_name and date_taken by student with ID=1
Anyone please?
You need to use the JOIN on the table_courses and table_student_course tables and then apply the Order By on cource_name to sort out by course name.And for selecting the particular student apply the Where clause as filter.
SELECT
t.course_name,
tsc.date_taken
FROM
table_courses t INNER JOIN table_student_course tsc
ON t.ID = tsc.course_ID
WHERE
tsc.student_ID = 1
ORDER BY
t.course_name
If you also want to get the students detail from the query then you need to join the 3 tables like below,
SELECT s.firstname, s.lastname, c.course_name, sc.date_taken
FROM table_courses c
INNER JOIN table_student_course sc ON c.ID = sc.course_ID
INNER JOIN table_student s ON sc.student_ID = s.ID
WHERE sc.student_ID = 1
ORDER BY c.course_name
SELECT s.firstname, s.lastname, c.course_name, sc.date_taken
FROM table_courses c
INNER JOIN table_student_course sc ON c.ID = sc.course_ID
INNER JOIN table_student s ON sc.student_ID = s.ID
WHERE sc.student_ID = 1
ORDER BY c.course_name
By using Inner Join on the tables "table_courses" & "table_student_course" we selects all rows from both tables as long as there is a match between the columns in both tables making sure the ids are the same. If the condition is met( student_id=1) then the query will return what is expected.
SELECT course_name, date_taken
FROM table_courses c INNER JOIN table_student_course sc ON c.id = sc.course_id
WHERE sc.student_ID = 1
ORDER BY course_name

SQL query to find the students in one course

There are three tables
Students, Courses and Registration
Students has id, name columns
Courses has also course.id, course.name
and there is third table joining the Students and Courses table
Registration : stu_id, course_id
One Student can take one or many courses.
I would like to find the name of Students registered in only one course.
Try with INNER JOIN
SELECT S.id, S.name
FROM students S
INNER JOIN registration R ON S.id = R.stu_id
GROUP BY S.id, S.name
HAVING COUNT(*) = 1
Like below:
SELECT s.id, s.name
FROM students s
LEFT JOIN registration r ON s.id = r.stu_id
GROUP BY s.id, s.name
HAVING COUNT(r.course_id) = 1
select s.*
from (
select r.stu_id stu_id
from Registration r
group by r.stu_id
having count(*) == 1) ra
join Students s on s.id = ra.stu_id;
This one is more efficient.
It's unlikely that your schema has null fields. Therefore, it doesn't matter which kind of join, inner or left, you use.

mysql substituting one columns value for another

I am trying to pull students and the courses they are registered for from a mysql database. I want to replace the course id with the course name in the output.
I have these tables and columns:
table: users_to_courses
columns: users_LOGIN and courses_ID
table: users
columns: login, name and surname
table: courses
columns: id and name
The tables tie together on the following:
users_to_courses.users_LOGIN = users.login
users_to_courses.courses_ID = courses.id
I am trying to get output of surname, name, courses (by name and not id) so it would look like Smith, John Chemistry, Physics, Composition
Here is what I am trying unsuccessfully:
SELECT
users.surname,
users.name,
users.login,
users_to_courses.users_LOGIN,
(select group_concat (courses.name) from courses
where users_to_courses.users_LOGIN = users.login and users_to_courses.courses_ID =
courses.id
GROUP BY users.surname) as courses
from courses join users_to_courses
on courses.id = users_to_courses.courses_ID
SELECT users.login, users.surname, users.name, courses.name
FROM users_to_courses
INNER JOIN users ON users_to_courses.users_LOGIN = users.login
INNER JOIN courses ON users_to_courses.courses_ID = courses.id
GROUP BY users_to_courses.users_LOGIN
It is not entirely clear how the users_to_courses table is organized, if it is one class per row entry, then you will end up with one row per class, but all the rows corresponding to the same user ID will be consecutive in the table...
If the users_to_courses.courses_ID entry is a list of multiple course ids this might not work...
Try this
Select
u.surname,
u.name,
u.login,
uc.users_LOGIN,
c.Name,
--added this line
group_concat (c.name)
From users_to_courses uc
JOIN users u ON uc.users_LOGIN = u.login
JOIN courses c ON uc.courses_ID = c.id
-- added this line
GROUP BY u.surname, u.name, u.login, uc.users_LOGIN
SELECT users.surname, users.name, users.login, users_to_courses.users_LOGIN from courses c
INNER JOIN users_to_courses uc ON uc.courses_ID = c.id
INNER JOIN login l ON uc.users_login = l.login

MySQL Join Query (possible two inner joins)

I currently have the following:
Table Town:
id
name
region
Table Supplier:
id
name
town_id
The below query returns the number of suppliers for each town:
SELECT t.id, t.name, count(s.id) as NumSupplier
FROM Town t
INNER JOIN Suppliers s ON s.town_id = t.id
GROUP BY t.id, t.name
I now wish to introduce another table in to the query, Supplier_vehicles. A supplier can have many vehicles:
Table Supplier_vehicles:
id
supplier_id
vehicle_id
Now, the NumSupplier field needs to return the number of suppliers for each town that have any of the given vehicle_id (IN condition):
The following query will simply bring back the suppliers that have any of the given vehicle_id:
SELECT * FROM Supplier s, Supplier_vehicles v WHERE s.id = v.supplier_id AND v.vehicle_id IN (1, 4, 6)
I need to integrate this in to the first query so that it returns the number of suppliers that have any of the given vehicle_id.
SELECT t.id, t.name, count(s.id) as NumSupplier
FROM Town t
INNER JOIN Suppliers s ON s.town_id = t.id
WHERE s.id IN (SELECT sv.supplier_id
FROM supplier_vehicles sv
WHERE sv.vehicle_id IN (1,4,6))
GROUP BY t.id, t.name
Or you could do an INNER JOIN (as your supplier join is INNER, but this will remove towns with no suppliers with those vehicles) and change the COUNT(s.id) TO COUNT(DISTINCT s.id)
If I remember correctly, you can put your second query inside the LEFT OUTER JOIN condition.
So for example, you can do something like
...
LEFT OUTER JOIN (SELECT * FROM Suppler s, Supplier_vehicles ......) s ON s.town_id=t.id
In that way you are "integrating" or combining the two queries into one. Let me know if this works.
SELECT t.name, count(s.id) as NumSupplier
FROM Town t
LEFT OUTER JOIN Suppliers s ON t.id = s.town_id
LEFT OUTER JOIN Supplier_vehicles v ON s.id = v.supplier_id
WHERE v.vehicle_id IN (1,4,6)
GROUP BY t.name