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:
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 (
SELECTthen exclude usingEXCEPT).- As MySQL doesn’t support
EXCEPTwe can useNOT INinstead.
- 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!