Complex query consisting 3 tables using mysql - mysql

I got 3 tables:
student
internship
student_internship
create table student(student_id varchar(45), student_name varchar(45));
create table internship(internship_id varchar(45), internship_name varchar(45));
and student_intership is the 'bridge' of the two tables.
student_intership(student_id, internship_id)
So, it is a many to many situation.
Situation:
I want to get the name of the internship and number of student, but
if there's no student for that internship, so it should have the following example:
intership_name | count(student_id)
--------------------------------
1. intern1 | 20
2. intern2 | 3
3. intern3 | 0
the code i have tried:
select internship.internship_id, count(student.student_id)
from student_internship, internship, student
where student_internship.student_id = student.student_id
and student_internship.internship_id = internship.internship_id
group by student_internship.internship_id;

Try this:
SELECT i.internship_name, COALESCE(COUNT(si.student_id), 0) AS cnt
FROM internship i
LEFT JOIN student_intership si ON i.internship_id = si.internship_id
GROUP BY si.internship_id, i.internship_name
The above query will return all records of table internship. It will return 0 for internship records having no relation to student records.

Quick stab:
SELECT st.student_id
,st.student_name
,si.InternshipCount
FROM student st
LEFT JOIN (
SELECT count(*) AS InternshipCount
,student_id
FROM student_internship s
INNER JOIN internship i
ON s.student_id = i.student_id
group by student_id
) AS si
ON st.student_id = si.student_id

Related

mysql select of select

Table Student
StudentID
StudentName
1
A
2
B
3
C
Table Book
BookID
BookName
1
Book1
2
Book2
3
Book3
Table BookAssignment
AssignID
BookID
StudentID
DateTime
1
1
1
2021-06-26
2
2
1
2021-07-01
3
1
2
2021-07-03
The result table should be
StudentID
StudentName
BookCount
1
A
2
2
B
1
3
C
0
How to get the result table in one SQL execution?
Left JOIN seems not an option since it eliminates StudentID 3
Just added another DateTime column to the BookAssignment table - What is SQL syntax to query the book count over the last 7 consecutive days (even for 0 book for day count)?
you need to use simple group by using left join between two tables:
select s.StudentID, s.StudentName , count(*) BookCount
from students s
left join books b
on s.StudentID = b.StudentID
group by s.StudentID, s.StudentName
I'd left join the student table on an aggregate query of the books and use coalesce to fill in the zeros:
SELECT s.StudentID, StudentName, COALESCE(cnt, 0)
FROM student s
LEFT JOIN (SELECT StudentID, COUNT(*) AS cnt
FROM books
GROUP BY StudentID) b ON s.StudentID = b.StudentID
You can also use a correlated subquery:
select s.*,
(select count(*)
from books b
where s.StudentID = b.StudentID
) as bookCount
from students s;
This has some advantages over using a join/group by approach:
You can trivially include all columns in the select. They don't have to repeated in the group by.
With an index on books(StudentID) this often has the best performance.
This avoids the outer aggregation, which can kill performance.
Adding another dimension (say the number of courses the student has) just works, without worrying about Cartesian Products.
select s.StudentID, s.StudentName ,(select count(*) from BookAssignment b where b.studentid = s.studentid) as BookCount
from students s

SQL -- Finding students taking 2 or more classes

I had this question in an interview, and I couldn't quite figure it out. I have three tables: a student table, a classes table and a table linking the other two. Here is a basic schema for them.
STUDENT
student_id | student_name
-----------+-------------
int | varchar(30)
CLASS
class_id | class_name
---------+-----------
int | varchar(30)
ROSTERS
student_id | class_id
-----------+---------
int | int
The rosters table shows which students are taking which classes. How do I write a query showing which students are taking 2 or more classes?
This query will do what you want:
SELECT s.student_name, COUNT(DISTINCT r.class_id) AS num_classes
FROM student s
LEFT JOIN rosters r ON r.student_id = s.student_id
GROUP BY s.student_id
HAVING num_classes >= 2
It counts all the distinct class_id values in roster for each student (num_classes) and returns only students with 2 or more (HAVING num_classes >= 2).
Note I've used a LEFT JOIN to catch all students, however since you want only those with more than 1 class this is not necessary and you could use a straight JOIN.
Also note that it's not necessary to JOIN the class table for this question, however if you wanted the names of the classes the student was taking you would need to.
You can use join, count, having and group by to get the required output:
select r.student_id,s.student_name,count(*)
from STUDENT s inner join ROSTERS r
on s.student_id = r.student_id
group by r.student_id,s.student_name
having count(*)>=2;
You need to join the two tables, STUDENT and ROSTERS (I have used inner join, if required, this can be changed as per requirements), counting the number of classes each student is taking.
SELECT s.student_name,
COUNT(r.class_id) AS count
FROM
STUDENT s
INNER JOIN
ROSTERS r
ON
r.student_id = s.student_id
GROUP BY
s.student_id
HAVING
count >= 2
this will work:
select s.*,r.*,c.*,count(*)
from
student s,
class c,
rosters r where
s.student_id=r.student_id and
c.class_id=r.class_id
group by s.student_id
having count(*)>=2
SELECT COUNT(class_id), student_id
FROM Rosters
GROUP BY student_id
HAVING COUNT(class_id) >=2
This is the simplest way to do it IMO.

SQL inner join wrong output from two tables

Students Table:
ID | Family_ID | Student_name | F_name
Families Table:
ID | F_name | Contact_No
i want to get all records from students where family_id is repeating.(basically i want to know students Brothers/Sisters records if there is any in student table.
i tried it this way but got wrong output;
SELECT students.*
FROM students
INNER JOIN families
ON families.id = students.family_id
ORDER BY families.id ASC
my query result in image: as you can see some ids are showing once others are more then once, but i think all should appear more then once.
If you want to see only relevant people you don't need to link it to the families table. You can group the student with family_id. Here is your query :
SELECT *
FROM Student
WHERE family_id IN (SELECT family_id
FROM students
GROUP BY family_id
HAVING COUNT(1)>1)
ORDER BY family_id
You could try using a join on subquery for the family_id that have more that one rows in students
SELECT students.*
FROM students
inner join (
select students.family_id
FROM students
group by students.family_id
having count(*)>1
) t on t. family_id = students.family_id

Return all courses a student is taking (Many to Many SQL Database example)

I'm fairly new to MySQL, and trying to understand the many-to-many relationship since these examples can popup in interviews
There are 3 tables, and since a Student can have many courses and a Course can have many students, this is a Many-to-Many relationship right?
The tables are
Student- has student ID, name, date of birth, and department.
Courses- Has ID, Name of course
Student_Courses- Has student_id, course_id
How would I display these 2 questions-
1) Given a studentID, return all the names of the courses the student is taking
2) Return the name of students who is taking X amount of courses or more (Ex. 4 or more courses).
Im trying to write queries on these, but I'm stuck...
In the case of selecting all of the courses for a given student ID you could try the following, which will return one row for each Course a Student is associated with.
select
s.name as StudentName,
c.name as CourseName
from `Student` as s
inner join `Student_Course` as sc on (sc.student_id = s.ID)
inner join `Course` as c on (c.ID = sc.course_id)
where
(s.`ID` = 'given_Student_ID_here')
;
As for selecting a list of the names of Students taking N or more courses, for this you might use an aggregating sub-select as a WHERE clause in which we reference one of the outer tables (i.e. [Student]) so that the result of the aggregation is personalised per Student record:
select
s.name as StudentName
from `Student` as s
where
(
(
select count(*)
from `Student_Course` as sc
inner join `Course` as c on (c.ID = sc.course_id)
where (sc.student_id = s.ID)
) >= 4
)
;
You might also consider an alternative approach to this second problem by using the GROUP BY and HAVING clauses:
select
s.name as StudentName
from `Student` as s
inner join `Student_Course` as sc on (sc.student_id = s.ID)
inner join `Course` as c on (c.ID = sc.course_id)
group by
s.name
having
count(*) >= 4
;

SQL COUNT Return Wrong Number

so I have a problem with my query. I have 2 tables:
courses:
The user_id in this table is the instructor of the course.
-----------------------------------------------------------------------
| course_id | user_id | course_name | other information |
-----------------------------------------------------------------------
| 6 | 1 | My Course 1 | ... |
-----------------------------------------------------------------------
my_courses:
The user_id in this table is a student of the course.
--------------------------------------------------
| user_id | course_id | created_at |
--------------------------------------------------
| 5 | 6 | [UNIX_TIMESTAMP] |
--------------------------------------------------
The my_courses contains the number of people that have joined that course. I want to get all the course info as well as the number of people that have joined a course. Everything returns as expected except the number of people that joined a course. This is the query I'm using:
SELECT
courses.*,
users.name, //This is the name of the instructor
users.last_name, //This is the last name of the instructor
COUNT(my_courses.user_id) as count_students
FROM courses
LEFT JOIN users
ON courses.user_id = courses.user_id
LEFT JOIN my_courses
ON courses.course_id = my_courses.course_id
WHERE courses.course_id = '6'
Like I said, this query returns the course info like normal but returns 3 as count_students when it should only return 1. Does anyone know why this is happening? Any help is greatly appreciated.
It's probably because of the JOIN condition for users. It should be:
LEFT JOIN users
ON courses.user_id = users.user_id
You should also add a GROUP BY clause in your query:
SELECT
c.*,
u.name,
u.last_name,
COUNT(mc.user_id) AS count_students
FROM courses c
LEFT JOIN users u
ON c.user_id = u.user_id
LEFT JOIN my_courses mc
ON c.course_id = mc.course_id
WHERE c.course_id = '6'
GROUP BY
<columns not in the aggregate function>
Additionally, alias your tables to improve readability.
JOIN operations cause combinatorial multiplication of rows. You need to summarize the student count from its own table like so.
SELECT course_id, COUNT(*) students
FROM my_courses
GROUP BY course_id
That gives you a result set with either one or zero rows per course_id. You can then join it to the rest of your query.
SELECT courses.*,
users.name, //This is the name of the instructor
users.last_name, //This is the last name of the instructor
aggr.count_students
FROM courses
LEFT JOIN users ON courses.user_id = courses.user_id
LEFT JOIN (
SELECT course_id, COUNT(*) students
FROM my_courses
GROUP BY course_id
) aggr ON courses.course_id = aggr.course_id
WHERE courses.course_id = '6'
That way you'll avoid multiple-counting your students for courses with, perhaps, more than one instructor.