sqlfilemaker

Select unrelated in a n:m relationship


I have a typical many-to-many relationship resolved by a join table, as shown here: enter image description here

For this question the sample data is:

People:

PersonID Name
P001 Alice
P002 Bob
P003 Carlos
P004 David

Courses:

CourseID Course
C001 Algebra
C002 Biology
C003 Chemistry

Attendance:

CourseID PersonID
C001 P001
C001 P002
C001 P003
C002 P002
C002 P003
C003 P003

The following query:

SELECT Courses.CourseID, Courses.Course, People.PersonID, People.Name
FROM Courses
INNER JOIN Attendance ON Courses.CourseID = Attendance.CourseID
INNER JOIN People ON Attendance.PersonID = People.PersonID

gives me a flat list of attendances by course:

C001,Algebra,P001,Alice
C001,Algebra,P002,Bob
C001,Algebra,P003,Carlos
C002,Biology,P002,Bob
C002,Biology,P003,Carlos
C003,Chemistry,P003,Carlos

Now I am trying to get a similar, but opposite result - i.e. a list of people who did NOT attend each course, which should look like this:

Expected result:

C001,Algebra,P004,David
C002,Biology,P001,Alice
C002,Biology,P004,David
C003,Chemistry,P001,Alice
C003,Chemistry,P001,Bob
C003,Chemistry,P004,David

I was hoping I could use something like:

SELECT Courses.CourseID, Courses.Course, People.PersonID, People.Name
FROM Courses
CROSS JOIN People
WHERE People.PersonID IN (
SELECT Attendance.PersonID
FROM Courses
INNER JOIN Attendance ON Attendance.CourseID = Courses.CourseID
)

but unfortunately that returns:

C001,Algebra,P001,Alice
C001,Algebra,P002,Bob
C001,Algebra,P003,Carlos
C002,Biology,P001,Alice
C002,Biology,P002,Bob
C002,Biology,P003,Carlos
C003,Chemistry,P001,Alice
C003,Chemistry,P002,Bob
C003,Chemistry,P003,Carlos

which suggests that the subquery is performed once for all courses, not individually for each course.

IMPORTANT:
The application I am using (Claris FileMaker) implements a very limited subset of the SQL-92 standard (and isn't very well documented to boot). I can only use the SELECT statement with a handful of clauses, and AFAICT only INNER JOIN, LEFT OUTER JOIN and CROSS JOIN are allowed. Also I haven't been able to implement a subquery anywhere except inside a WHERE clause.

Is it at all possible to return the expected result using only plain, basic SQL?


Solution

  • something like this should work

    select *
    from Courses as c cross join People as p
    left outer join Attendance as a
    on (c.CourseID = a.CourseID and p.PersonID = a.PersonID)
    where a.AttendId is null
    

    the main trick is to generate Cartesian product of all courses/people first - ie all combinations

    then, we use left join to find matching attendance records, but because it is LEFT join - it will not exclude non matching records, all non matching combinations will have null as value from attendance table