SQL Query INTERSECTS - Relational Algebra - mysql

I am trying to convert the following 2 relational algebra into standard english but I am having trouble understand what exactly the INTERSECTS does
I know that INTERSECT finds the values that occur in both select statements
I have managed to find its SQL query, but I just can't figure out what it asks in plain english
Would anyone be able to help?
Relational Algebra:
Πstudent_name(σcourse id=“CS1850“(student Natural Join enrolled)) ∩
Πstudent_name(σcourse id=“CS1840“(student Natural Join enrolled))
SQL:
SELECT student_name FROM student NATURAL JOIN enrolled WHERE
course_id='CS1850' INTERSECT SELECT student_name FROM student
NATURAL JOIN enrolled where student_name='CS1840'
Second Expression:
Πstudent_name(σe1.student
id=e2.student_id∧e1.course_id<>e2.course_id(student natural join
(ρe1(enrolled) × ρe2(enrolled))))
SELECT student_name FROM student NATURAL JOIN enrolled e1 NATURAL JOIN
e2 WHERE e1.student_id = e2.student_id

First, don't use NATURAL JOIN. It is an error waiting to happen. It matches tables based on the names of columns in common. It doesn't even use properly declared foreign key relationships. Instead, use USING or ON to be explicit about the columns used for joining.
Second, you don't need INTERSECT. There are two typical methods that use GROUP BY or JOIN.
In English, the query gets the students that are enrolled in both classes.
SELECT s.student_name
FROM student s JOIN
enrolled e
USING (student_id) -- or whatever
WHERE e.course_id IN ('CS1850', 'CS1840')
GROUP BY s.student_name
HAVING COUNT(*) = 2;
Note: This assumes that two students do not have the same name.
An alternative just uses JOIN:
SELECT s.student_name
FROM student s JOIN
enrolled e1
USING (student_id) JOIN
enrolled e2
USING (student_id)
WHERE e1.course_id = 'CS1850' AND
e2.course_id = 'CS1840';
If the underlying tables do not have duplicates, then this doesn't need a GROUP BY or DISTINCT.

Related

Having trouble a query and specifically with joins

The code below is completely wrong and does not work at all. Im basically trying to look through my tables and compile a list of DeptName and the total student number for a department where a department has more than 40 students.
Im confused about joins in general and if someone could explain and show where im going wrong. im sure there is also other problems so any help with them would help
So basically one department is connected to one module, and a student is enrolled in a module. A student cannot take a module outside of their department. So each student should have one module that connects to one department
All of the ID fields in other tables are foreign keys as you can guess and changing the tables is not what I want to do here I just want to do this query as this stands
Relevant tables columns
Table Department DeptID, DeptName, Faculty, Address
Table Modules ModuleID, ModuleName, DeptID, Programme
Table Students StudentID,StudentName,DoB,Address,StudyType,`
Table Enrolments EID,StudentID,ModuleID,Semester,Year
SELECT Department.DeptName, COUNT(Student.StudentID) AS 'No of Students' FROM Department LEFT JOIN Module ON Department.DeptID= Module.DeptID LEFT JOIN Enrolment ON Module.ModuleID= Enrolment.StudentID LEFT JOIN Student.StudentID
GROUP BY(Department.DeptID)
HAVING COUNT(Student.StudentID)>=40
I have not included every table here as there are quite a lot.
But unless i've got this completely wrong you don't need to access a ModuleID in a staff table for the module they teach or something not relevant to this at all. As no student or Dept details are in there.
If that is the case i will fix it very quickly.
SELECT Department.DeptName, COUNT(Student.StudentID) AS 'No of Students'
FROM Department
LEFT JOIN Module
ON Department.DeptID= Module.DeptID
LEFT JOIN Enrolment
-- problem #1:
ON Module.ModuleID= Enrolment.StudentID
-- problem #2:
LEFT JOIN Student.StudentID
-- problem #3:
GROUP BY(Department.DeptID)
HAVING COUNT(Student.StudentID)>=40
You're joining these two tables using the wrong field. Generally when the modeling is done correctly, you should use USING instead of ON for joins
The right side of any JOIN operator has to be a table, not a column.
You have to group by every column in the select clause that is not part of an aggregate function like COUNT. I recommend that you select the DeptID instead of the name, then use the result of this query to look up the name in a subsequent select.
Note : Following code is untested.
WITH bigDepts AS (
SELECT DeptId, COUNT(StudentID) AS StudentCount
FROM Department
JOIN Module
USING ( DeptID )
JOIN Enrolment
USING ( ModuleID )
JOIN Student
USING ( StudentID )
GROUP BY DeptID
HAVING COUNT(StudentID)>=40
)
SELECT DeptID, DeptName, StudentCount
FROM Department
JOIN bigDepts
USING ( DeptID )
Instead of left join you need to use inner join since you need to select related rows only from those three tables.
Groupy by and having clause seems fine. Since you need departments with more than 40 students instead of >= please use COUNT(e.StudentID)>40
SELECT d.DeptName, COUNT(e.StudentID) AS 'No of Students' FROM Department d INNER JOIN Module m ON d.DeptID= m.DeptID inner JOIN Enrolment e ON m.ModuleID= e.StudentID LEFT JOIN Student.StudentID
GROUP BY(d.DeptName)
HAVING COUNT(e.StudentID)>40
So your join clause was a bit iffy to students as you wrote it, and presumably these should all be inner joins.
I've reformatted your query using aliases to make it easier to read.
Since you're counting the number of rows per DeptName you can simply do count(*), likewise in your having you are after counts greater than 40 only. Without seeing your schemas and data it's not possible to know if you might have duplicate Students, if that's the case and you want distinct students count can amend to count(distinct s.studentId)
select d.DeptName, Count(*) as 'No of Students'
from Department d
join Module m on m.DeptId=d.DeptId
join Enrolment e on e.StudentId=m.ModuleId
join Students s on s.StudentId=e.studentId
group by(d.DeptName)
having Count(*)>40
Also, looking at your join conditions, is the Enrolement table relevant?
select d.DeptName, Count(*) as 'No of Students'
from Department d
join Module m on m.DeptId=d.DeptId
join Students s on s.StudentId=m.moduleId
group by(d.DeptName)
having Count(*)>40

SQL - Selecting items from table based on data in other tables?

In the above relational schema, how would I do the following?:
List the names of all students that have a higher GPA than the minimum required GPA for the major they have applied for.
A couple of joins should do the trick:
SELECT s.*
FROM Student s
JOIN Apply a ON s.sId = a.sId
JOIN MinimumGPA m on m.major = a.major
WHERE s.gpa > m.mingpa
You use JOIN and NATURAL JOIN(this one is not required but i like it),
with join you ufse 2 tables giving 2 columns that need to be equal(you specify them in the WHERE), NATURAL JOIN does the same but assuming you have 1 or more columns with the same name(those are the ones natural join uses like if they were delared in the where)
So first you fuse MinimumGPA and Apply(they have 2 cols with the same name so natural join)
Select * FROM MinimumGPA NATURAL JOIN Apply
then, since you awhere asked for the NAMES of the students, you fuse that new table(giving it a name, in this case i used MinimumGPAApply, you could name it "the dogtable" if you wanted) with students, since the name on the columns isnt the same you use JOIN and specify the columns in the where, also you add the gpa condition
Select sName from Student JOIN (The first query) As MinimumGPAApply WHERE Student.sId = MinimumGPAApply.sID AND Student.GPA > MinimumGPAApply.minGPA
So at the end you end with somethign like this:
Select sName from Student JOIN (Select * FROM MinimumGPA NATURAL JOIN Apply) As MinimumGPAApply WHERE Student.sId = MinimumGPAApply.sID AND Student.GPA > MinimumGPAApply.minGPA
select s.Cname,s.gpa as studentGpa ,mg.mingpa as mingpaRequired from student s
inner join apply a on s.sid=a.sid
inner join major m on m.major=a.major
inner join minimumGPA mg on mg.major=m.major
where mg.mingpa<s.gpa

How to do an Intersect query in MySQL?

I have two sql queries and I need the intersect between then. I know mysql does not have the intersect operand, so how could I do it? Tried some solutions I found online but they all return some syntax error. The queries are:
SELECT person_name FROM person NATURAL JOIN (
SELECT * FROM movie NATURAL JOIN role WHERE movie_name = 'Alien'
) alias1
SELECT person_name FROM person WHERE gender = 'f'
You can JOIN all three tables together, and apply both WHERE clauses.
Here is an example. It assumes that your movie table joins to your role table on a column called movie_id, as well as an assumption that you have a person_id column in role and person to join them. You'll need to change this query to use the columns that your tables use.
SELECT
p.person_name
FROM
movie m
INNER JOIN role r
ON (m.movie_id=r.movie_id)
INNER JOIN person p
ON (r.person_id=p.person_id)
WHERE
m.movie_name='Alien'
AND p.gender='f'
Edit: I've used INNER JOIN here, with a guess at the columns to join on, as opposed to keeping the OP's NATURAL JOIN. As the comments to the OP have mentioned, best practice is to explicitly identify the joins, and not rely on a NATURAL JOIN.

MySQL Join with looped reference query

I have a slightly complex table structure that I'm trying to query for a search function, but my queries keep timing out. Basically, it's a book search, and I'm focusing on the subject portion of that search.
The subjects table is simple (id and title), but there's a link table that refers it back to itself called subjects_subjects, which complicates things.
**subjects_subjects**
id (key)
subject_id (reference to subjects table)
see_subject_id (another reference to subjects table)
The reason for the looping reference is to catch subjects that don't contain any books, but point to subjects that do. For example, there's no books under the 'Travel' subject, so that subject has a link to 'Explorers' and 'Voyages' that do contain books. The point is to make searching easier.
So what I'm trying to do is allow the user to search for 'Travel', but return results from 'Explorers' and 'Voyages'. Here's my query that times out:
SELECT
BK.id,
BK.title
FROM
books BK
LEFT OUTER JOIN
books_subjects BS
ON BS.book_id = BK.id
WHERE
BS.subject_id IN (1639,3173)
OR BS.subject_id IN
(
SELECT
SS.see_subject_id
FROM
subjects_subjects SS
WHERE
SS.subject_id IN (1639,3173)
)
GROUP BY
BK.books_id
Extra info: There are 17000 books and over 3000 subjects in the database, with roughly 84000 book/subject references.
Can anyone help me figure out where am I going wrong here?
You're doing two things that MySQL optimizes poorly:
OR in the WHERE clause.
IN (SELECT ...)
Instead of OR, use two queries that you combine with OR. And instead of IN (SELECT ...) use a JOIN.
Also, you shouldn't use LEFT JOIN if you don't need to return rows from the first table with no matches in the second table, use INNER JOIN.
SELECT b.id, b.title
FROM books AS b
JOIN books_subjects AS bs ON bs.book_id = b.id
WHERE bs.subject_id IN (1639, 3173)
UNION
SELECT books AS b
JOIN books_subjects AS bs ON bs.book_id = b.id
JOIN subjects_subjects AS ss ON bs.subject_id = ss.see_subject_id
WHERE ss.subject_id IN (1639, 3173)

how the inner join can be nested?

I am newbie in SQL and practicing queries for my university assignments...I have three tables in the database:
1. student:
studentid pk
fname
school
2. books:
bookid pk
bookname
3. issue:
issueid pk
studentid fk
bookid fk
fine
and I am trying to solve this query:
select the bookname and the average fine for those students who have issued two or more books.
I have tried using nested INNER JOINs as:
SELECT fname,bookname,AVG(fine) FROM
student INNER JOIN ( books INNER JOIN issue ON books.bookid=issue.bookid) ON
student.studentid=issue.issueid
GROUP BY fname
HAVING COUNT(bookid)>1;
but failed.Is there any mistake or is it totally wrong to nest JOINs as above? Or please hint me if there is an another way to do this using JOIN.
You can do two separate joins:
SELECT fname,bookname,AVG(fine)
FROM student
INNER JOIN issue ON student.studentid=issue.issueid
INNER JOIN books ON books.bookid=issue.bookid
GROUP BY fname
HAVING COUNT(bookid)>1;
Another possibility is to join on several tables in the same INNER JOIN:
SELECT fname,bookname,AVG(fine)
FROM student
INNER JOIN (issue, books) ON (student.studentid=issue.issueid AND books.bookid=issue.bookid)
GROUP BY fname
HAVING COUNT(bookid)>1;
The reason your query was wrong is simply that you did not use the right syntax...: there is no such thing as nested joins.
This page is the MySQL documentation page for joins. You will find many examples of how to handle joins.
This one should work. You can append INNER JOINs after each other, and use the values of any previously called tables after the ON parameter.
SELECT student.fname, books.bookname, AVG(issue.fine)
FROM student
INNER JOIN issue ON issue.studentid = student.studentid
INNER JOIN books ON books.bookid = issue.bookid
GROUP BY student.fname
HAVING COUNT(books.bookid)>1