How can I do this relational division query? - mysql

I want to do a relational division query. First, here is the structure of each table:
Student
id
name
Course
id
name
Student_passed_course (junction table that stores who passed which course)
id_student
id_course
Basically, what I want is to get the names of the students that have passed all the courses that exist in table Course using JOIN (or LEFT JOIN, etc). I already implemented a solution using NOT EXISTS.
Also this is the equation I made.

A solution based on JOIN could be as follow:
SELECT s.name AS student_name, c.name as course_name FROM (
SELECT s_id, c_id, SUM(c_exists) AS c_exists, SUM(c_taken) as c_taken, SUM(c_exists)-SUM(c_taken) as not_taken FROM (
SELECT s.id as s_id, c.id as c_id, 1 as c_taken, 0 as c_exists FROM student s JOIN student_passed_course spc ON spc.id_student = s.id JOIN course c ON c.id = spc.id_course
UNION ALL
SELECT s.id as s_id, c.id as c_id, 0 as c_taken, 1 as c_exists FROM student s JOIN course c
) X group BY s_id, c_id) Y
JOIN student s ON s.id = s_id JOIN course c ON c_id = c.id
WHERE not_taken = 1;
I don't know if this would be more or less efficient than your solution.

Related

How to refer without foreign key?

Create two tables:
Course(Course_id(primary key), Course_name)
Student(Roll_no(primary key), Name,Course_id(Foreign key)) and Retrieve the names of all the students who are admitted in the course 'BSC'.
let, the course_id for BSC be 105.
For which the query will be:
SELECT Name FROM Student WHERE Course_id = 105
Can I query for the name of the student without knowing the Course_id (just using the Course_name) ?
You could use an inner join between the tables
SELECT s.Name
FROM Student s
INNER JOIN Course c on c.course_id = s.Course_id
WHERE c.Course_name = 'your_course_name'
or using like
SELECT s.Name
FROM Student s
INNER JOIN Course c on c.course_id = s.Course_id
WHERE c.Course_name like 'your_course_name'
Or use WHERE IN (...)
SELECT
Student.Name
FROM
Student
WHERE
Student.Course_id IN (
SELECT
Course.cource_id
FROM
Course
WHERE
Course.Name = 'BSC'
)
Yes, you can.
SELECT st.Name
FROM Student st
INNER JOIN Course c on c.course_id = st.Course_id
WHERE
c.Course_name = 'Course_Name';

Matching one value in one column with more than one values in other column in SQL

Suppose I have three tables
Student Student_Interest Interest
======= ================ ========
Id Student_Id Id
Name Interest_Id Name
Where Student_Interest.Student_Id refers to Student.Id
and Student_Interest.Interest_Id refers to Interest.Id
Let's say we have three kinds of interest viz. "Java", "C", "C++" and "C#" and there are some entries in the student table and their respective interest mapping entries in the Student_Interest table. (A typical many-to-many relationship)
How can we get the list of students that have both "Java" and "C" as their interests?
I already see a couple of correct answers, but anyway, here is my one:
select s.* from student s
join (
select si.student_id from student_interest si join interest i on i.id = si.interest_id
where i.name in ('Java','C') group by si.student_id having count(*) = 2
) iv on iv.student_id = s.id
Simply get the Java and C records from student_interest, group by student and see if you get the complete number of interests for a student. With such students found you can display data from the student table.
select *
from student
where id in
(
select student_id
from student_interest
where interest_id in (select id from interest where name in ('Java', 'C'))
group by student_id
having count(distinct interest_id) = 2
);
EDIT: You've asked me to show a query with EXISTS. The straight-forward way would be:
select *
from student
where exists
(
select *
from student_interest
where student_id = student.id
and interest_id = (select id from interest where name = 'Java')
)
and exists
(
select *
from student_interest
where student_id = student.id
and interest_id = (select id from interest where name = 'C')
);
For every interest an additional EXISTS clause. If, however, you want to convert the IN query above to an EXISTS query, so to have only one EXISTS clause, you get:
select *
from student
where exists
(
select student_id
from student_interest
where student_id = student.id
and interest_id in (select id from interest where name in ('Java', 'C'))
group by student_id
having count(distinct interest_id) = 2
);
I find the IN clause more readable, but that's a matter of taste, I guess.
How can we get the list of students that have both "Java" and "C" as
their interests?
We can write t(t.c,...) to say that row (t.c,...) is in table t. Let's alias Student to s, Student_Interest to sij and sic and Interest to ij and ic. We want rows (s.Id,s.Name) where
s(s.Id,s.Name)
AND sij(sij.Student_Id,sij.Interest_Id) AND s.Id = sij.Student_Id
AND sic(sic.Student_Id,sic.Interest_Id) AND s.Id = sic.Student_Id
AND ij(ij.Id,ij.Name) AND ij.Id=sij.Interest_Id AND ij.Name = 'Java'
AND ic(ic.Id,ic.Name) AND ic.Id=sic.Interest_Id AND ic.Name = 'C'
So:
select s.Id,s.Name
from Student s
join Student_Interest sij on s.Id = sij.Student_Id
join Student_Interest sic on s.Id = sic.Student_Id
join Interest ij on ij.Id=sij.Interest_Id AND ij.Name = 'Java'
join Interest ic on ic.Id=sic.Interest_Id AND ic.Name = 'C'
You will need to join all the three tables to get the information as below:
SELECT s.*
FROM Student s, Student_Interest si, Interest i, Interest sin
WHERE s.Id = si.Student_Id
AND i.Id = si.Interest_Id
AND sin.Id = si.Interest_Id
AND i.Name = 'Java'
AND sin.Name = 'C'
Do a JOIN with all the tables. You need to GROUP BY the column Name to get both interest like below. See a demo fiddle http://sqlfiddle.com/#!2/e80391/13
select s.Name
from student s
join Student_Interest si on s.id = si.Student_Id
join Interest i on si.Interest_Id = i.id
join Interest ii on si.Interest_Id = ii.id
group by s.Name
having count(*) > 1

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 Select From 4 Different Tables

I have a MySql query problem. The tables I'm using are very large, so I listed a simple example that I can then use in my more complex table structure.
Lets say the tables are the following.
House(id, name)
Person(id, name, house_id)
Car(id, name, person_id, type)
CarEngine(id, name, hp)
Each Person belongs to a House. Each Car belongs to a Person. Each Car has a CarEngine with the same primary key (id).
How would I select only the name from each table while selection all the information from CarEngine from these tables efficiently where Car type is truck?
select p.name as PersonNamee, h.name as HouseName, c.name as CarName,
e.id as CarID, e.name as EngineName, e.hp
from Person p
inner join House h on p.house_id = h.id
inner join Car c on p.id = c.person_id
inner join CarEngine e on c.id = e.id
where c.type = 'truck'

How to get data from 4 tables in 1 sql query?

I have the following database schema:
table courses:
id
tutor_id
title
table course_categories:
id
category_id
course_id
table categories:
id
name
table tutors:
id
name
table subscribers:
id
course_id
user_id
I need to make 1 sql to get a course with all it's categories, and the tutor for that course and the number of subscribers for that course. Can this be done in 1 query? Should this be done using stored procedures?
With this query you get what you want:
select co.title as course,
ca.name as category,
t.name as tutor,
count(s.*) as total_subscribers
from courses co
inner join course_categories cc on c.id = cc.course_id
inner join categories ca on cc.category_id = ca.id
inner join tutors t on co.tutor_id = t.tutor_id
left join subscribers s on co.id = s.course_id
where co.title = 'Cat1'
group by co.title, ca.name, t.name
I used left join on subscribers because there might be no one for a given course. I'm assuming that all the other tables have data on it for every course, categorie and tutor. If not, you can user left join as well but then you'll have data with null.
It can be done. You need to look up select and the use of join. See select and join to help complete the assignment
select cou.title, cat.name, tu.name, count(sub.user_id) from courses cou, course_categories cca, categories cat, tutors tu, subscribers sub where cou.id = cca.id and cat.id = tu.id and tu.id = sub.id group by cou.title, tu.name;