Can someone help with this homework problem:
Find IDs and names of students who took CS-101 and did not take CS-319.
These are the two tables that I can use for this assignment:
x
TAKES (Table)
ID (PK)
course_id (PK)
sec_id (PK)
semester (PK)
year (PK)
grade
STUDENT (Table)
ID (PK)
name
dept_name
tot_cred
I tried this but was unable to successfully exclude the students who who took “CS-319”
SELECT ID, name, course_id
FROM (
SELECT ID, name, course_id
FROM student NATURAL JOIN takes
WHERE course_id != "CS-319") AS T1
WHERE course_id = "CS-101"
Advertisement
Answer
There are at least three separate ways to solve this problem in SQL.
- Set operations (
SELECT
then exclude usingEXCEPT
).- As MySQL doesn’t support
EXCEPT
we can useNOT IN
instead.
- As MySQL doesn’t support
- Anti-join (
LEFT OUTER JOIN
). - Join and discriminate on
COUNT
.
Approach 1: SELECT
then exclude using EXCEPT
:
SELECT
student_id
FROM
takes
WHERE
course_id = 'CS-101'
EXCEPT
SELECT
student_id
FROM
takes
WHERE
course_id = 'CS-319'
Then use this as an inner-query to JOIN
on student information:
SELECT
iq.student_id,
student.name
FROM
(
SELECT
student_id
FROM
takes
WHERE
course_id = 'CS-101'
EXCEPT
SELECT
student_id
FROM
takes
WHERE
course_id = 'CS-319'
) AS iq
INNER JOIN students ON iq.student_id = students.student_id
Approach 1 for MySQL (using NOT IN
):
SELECT
iq.student_id,
student.name
FROM
(
SELECT
student_id
FROM
takes
WHERE
course_id = 'CS-101'
AND
student_id NOT IN
(
SELECT
student_id
FROM
takes
WHERE
course_id = 'CS-319'
)
) AS iq
INNER JOIN students ON iq.student_id = students.student_id
Approach 2: Anti-join:
SELECT
s.student_id,
s.name
FROM
students AS S
LEFT OUTER JOIN
(
SELECT
student_id,
COUNT(*) AS Cs101Count
FROM
takes
WHERE
course_id = 'CS-101'
GROUP BY
student_id
) AS c1 ON s.student_id = c1.student_id
LEFT OUTER JOIN
(
SELECT
student_id,
COUNT(*) AS Cs319Count
FROM
takes
WHERE
course_id = 'CS-319'
GROUP BY
student_id
) AS c2 ON s.student_id = c2.student_id
WHERE
c1.Cs101Count > 0
AND
c2.cS319Count = 0
Approach 3: Join and discriminate on COUNT
:
SELECT
s.student_id,
s.name
FROM
students AS S
INNER JOIN
(
SELECT
student_id,
COUNT( CASE WHEN course_id = 'CS-101' END ) AS Cs101Count,
COUNT( CASE WHEN course_id = 'CS-319' END ) AS Cs319Count
FROM
takes
GROUP BY
student_id
) AS c ON c.student_id = s.student_id
WHERE
c.Cs101Count > 0
AND
c.Cs319Count = 0
More approaches:
There are other alternatives using WHERE EXISTS
, WHERE NOT EXISTS
, and so on. One of the problems (or challenges) when using SQL is there are different ways to query the source data to get the exact same results. Have fun!