0

Question:

How can I display the student name, the department name and the course name of every course that the student has not taken?

I have tried using generating a cartesian product like this:

SELECT  
    NAME, student.dept_name, title
FROM 
    student
JOIN 
    (SELECT 
         id, title
     FROM 
         takes, course
     WHERE 
         takes.course_id <> course.course_id) a ON student.id = a.id
ORDER BY 
    NAME ASC, title ASC

And it works fine as long as the student only takes 1 course, but not if he attends to more than one

Can you guys provide a solution ? Thanks in advance

Database relations

The desired output is a connection of the values in this format

| name | dept_name | title (course name) |
13
  • 1
    Kindly share schema and desired output. Commented Nov 17, 2017 at 7:18
  • I've put the schema and the desired output :) Commented Nov 17, 2017 at 7:30
  • Your query is NOT a Cartesian product (which is a CROSS JOIN) Commented Nov 17, 2017 at 7:48
  • Logically the cartesian product/ cross join should be on student to courses. Commented Nov 17, 2017 at 7:50
  • Please use ansi join syntax. That is what they should learn you in school, not the old scool way Commented Nov 17, 2017 at 7:55

3 Answers 3

1

The fact that there is no connection between courses an student is not an issue you need to know all the courses a student could take so given

student

Name       CourseId
---------- ----------
Zishan     1,2,3
Ellen      2,3,4

(2 row(s) affected)

Course

courseid    coursename
----------- ----------
1           java
2           C++
3           oracle
4           dot net

Courses_taken

sid                  cid
-------------------- -----------
zishan               1
zishan               2

(2 row(s) affected)

a cross join like this

select s.Name Studentname, c.courseid cid,c.coursename CourseNAme
from student s
cross join course c

Results in

Studentname cid         CourseNAme
----------- ----------- ----------
Zishan      1           java
Zishan      2           C++
Zishan      3           oracle
Zishan      4           dot net
Ellen       1           java
Ellen       2           C++
Ellen       3           oracle
Ellen       4           dot net

Joining to courses_taken

select sc.Studentname,sc.Coursename,ct.cid
from
(
select s.Name Studentname, c.courseid cid,c.coursename CourseNAme
from student s
cross join course c
) sc
left join courses_taken  ct on sc.StudentName = ct.sid and sc.cid = ct.cid

results in this

Studentname Coursename cid
----------- ---------- -----------
Zishan      java       1
Zishan      C++        2
Zishan      oracle     NULL
Zishan      dot net    NULL
Ellen       java       NULL
Ellen       C++        NULL
Ellen       oracle     NULL
Ellen       dot net    NULL

To exclude the courses taken a null where condition

select sc.Studentname,sc.Coursename,ct.cid
from
(
select s.Name Studentname, c.courseid cid,c.coursename CourseNAme
from student s
cross join course c
) sc
left join courses_taken  ct on sc.StudentName = ct.sid and sc.cid = ct.cid
where ct.cid is null

Results in

Studentname Coursename cid
----------- ---------- -----------
Zishan      oracle     NULL
Zishan      dot net    NULL
Ellen       java       NULL
Ellen       C++        NULL
Ellen       oracle     NULL
Ellen       dot net    NULL

(6 row(s) affected)
Sign up to request clarification or add additional context in comments.

Comments

0

Do a query that gets all combos of student and courses by doing Cartesian join, then remove all the courses a student has done by doing the 2nd query below the except. This handles a student who has not done any courses yet, which is why a not exists doesn’t work.

Select s.id, c.title 
From student s cross join courses c
Except
Select t.id, c.title 
From takes t join courses c on t.course_id = c.course_id

Comments

0
select
    s.*, c.*
from students s
cross join courses c
left join takes t on s.student_id = t.id -- naming looks weird
                 and c.course_id = t.course_id
where t.student_id IS NULL
order by
  s.name, c.title -- or similar

A cross join forms a Cartesian product that forms all combinations of student + course. Outer join that large potential set to the actual set of students enrolled to courses. Then, the un-joined rows constitute the courses that have not been taken by (each) student.

If the questions has a small "twist" and that all you need is the list of un-taken courses then:

select distinct
    c.*
from students s
cross join courses c
left join takes t on s.student_id = t.id -- naming looks weird
                 and c.course_id = t.course_id
where t.student_id IS NULL
order by
  c.title -- or similar

Will simplify the result to just the untaken courses.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.