SQL для получения кортежей несвязанных записей

Представьте себе такую ​​базу данных:

тестовая база данных

И ищу всех студентов - наборы учителей, которые не разделяют никаких курсов. Результат должен быть (Student.Name - Teacher.Name) кортеж.

Эта идея близка к решению, но не совсем то, что хотел.

--- The students that go to some courses 
SELECT S.FIRSTNAME, S.LASTNAME
FROM STUDENTS S
JOIN STU_COU SC ON S.STUDENTID = SC.STUDENTS_STUDENTID

UNION ALL

--- teachers not in attended courses 
SELECT T.FIRSTNAME, T.LASTNAME
FROM TEA_COU TC
JOIN TEACHERS T ON T.TEACHERID = TC.TEACHERS_TEACHERID
WHERE TC.COURSES_COURSEID NOT IN (
  SELECT C.COURSEID
  FROM STUDENTS S
  JOIN STU_COU SC ON S.STUDENTID = SC.STUDENTS_STUDENTID
  JOIN COURSES C ON C.COURSEID = SC.COURSES_COURSEID
);

Тестовые данные, такие как:

  • TEACHER A преподает курсы CS и MATH;
  • СТУДЕНТ А идет на курс CS;
  • СТУДЕНТ Б ходит на курсы ЛИТЕРАТУРЫ и СПОРТА;

Результат будет

STUDENT B - TEACHER A

Ищите универсальное решение, поэтому не указана конкретная база данных.

4 ответа

Решение

Вы можете начать с перекрестного соединения, а затем удалить любую пару, имеющую отношение:

SELECT     s.firstname, s.lastname, t.firstname, t.lastname
FROM       students s
CROSS JOIN teachers t
WHERE      NOT EXISTS (SELECT *
                       FROM   stu_cou sc
                       JOIN   tea_cou tc ON sc.courses_courseid = 
                                            tc.courses_courseid
                       WHERE  sc.students_studentid = s.studentid AND
                              tc.teachers_teacherid = t.teacherid)

В Oracle вы можете использовать minusили SQL Server или PostgreSQL except оператор set: (функционально эквивалентный)

select s.firstname as stud_fname,
       s.lastname  as stud_lname,
       t.firstname as teac_fname,
       t.lastname  as teac_lname
  from students s
 cross join teachers t
minus
select s.firstname,
       s.lastname,
       t.firstname,
       t.lastname
  from students s
  join stu_cou sc
    on s.studentid = sc.students_studentid
  join courses c
    on sc.courses_courseid
  join tea_cou tc
    on c.courseid = tc.courses_courseid
  join teachers t
    on tc.teachers_teacherid = t.teacherid

Это похоже на решение @Mureinik, но позволяет избежать CROSS JOIN:

SELECT     s.firstname, s.lastname, t.firstname, t.lastname
FROM       students s
JOIN teachers t ON NOT EXISTS (
    SELECT *
    FROM   stu_cou
    JOIN   tea_cou tc ON sc.courses_courseid = tc.courses_courseid
    WHERE  sc.students_studentid = s.studentid
    AND tc.teachers_teacherid = t.teacherid
    );

Вот решение, очень похожее на @Brian DeMilia, которое работает для MySQL:

SELECT
    s.firstname ,
    s.lastname ,
    t.firstname ,
    t.lastname  
from
    students s   
cross join
    teachers t  
where
    (
        s.firstname ,  s.lastname ,  t.firstname ,  t.lastname 
    ) NOT IN  (
        select
            s.firstname,
            s.lastname ,
            t.firstname,
            t.lastname  
        from
            students s  
        inner join stu_cou sc  
                on s.studentid = sc.students_studentid  
        inner join courses c  
                on sc.courses_courseid = c.courseid  
        inner join tea_cou tc  
                on c.courseid = tc.courses_courseid  
        inner join teachers t  
                on tc.teachers_teacherid = t.teacherid 
        );
Другие вопросы по тегам