выбор строк, которых нет в таблице, созданной с помощью нескольких операторов соединения

#sql

#sql

Вопрос:

У меня есть такая схема:

 create table AppUser (id int, name varchar(255));
insert into AppUser (id, name) values ('1','sashan');
insert into AppUser (id, name) values ('2','someone');
insert into AppUser (id, name) values ('3','mckenzie');
create table school (id int, name varchar(255));
insert into School (id, name) values ('1','pascoe high');
insert into School (id, name) values ('2','highbury');
insert into School (id, name) values ('3','hilton');
insert into School (id, name) values ('4','melbourne girls');
create table Student(SchoolId int, UserId int);
insert into Student (SchoolId, UserId) values ('2','1');
insert into Student (SchoolId, UserId) values ('3','1');
insert into Student (SchoolId, UserId) values ('4','3');
 

Таблица AppUser — это информация о пользователе. Школьная таблица — это информация о школе. Таблица Student связывает школу с пользователем. Я хочу выбрать все школы, в которых нет «sashan».

Я придумал это:

 select *
from School
left join Student
       on Student.SchoolId = School.Id
left join AppUser
       on AppUser.id = Student.userid
where AppUser.name is null
   or AppUser.name != 'sashan';
 

Но хотите знать, есть ли лучший способ сделать это.

Если вы хотите воспроизвести таблицу и протестировать sql, используя код в этом вопросе, см. http://www.sqlfiddle.com /

Ответ №1:

Вы могли бы попробовать это:

 SELECT *
FROM   School
WHERE  id NOT IN (SELECT SchoolId
                  FROM   Student
                  JOIN   AppUser
                    ON   Student.UserId = AppUser.id
                   AND   name = 'sashan')
 

Запрос в скобках выбирает всю школу, в которой находится «сашан».
Выбрав все школы, не входящие в состав этих школ, вы получите те, которые вы запросили.

Ответ №2:

Вы все делаете правильно. Вот альтернативная версия, использующая NOT EXISTS cause

 SELECT s.*
FROM school s
WHERE NOT EXISTS 
  (SELECT 1 
   FROM student st 
   JOIN AppUser au ON au.name != 'sashan' AND au.id = st.userId 
   WHERE s.id = st.schoolId)
 

Если у вас правильные индексы, то оба запроса должны работать довольно хорошо

Ответ №3:

Я бы использовал not exists :

 select s.*
from School s 
where not exists (select 1
                  from Student st join
                       AppUser au
                       on au.id = st.userid
                  where st.SchoolId = s.Id and
                        au.name = 'sashan' 
                 );
 

Ваша версия не совсем правильная, потому что условие on 'sashan' должно быть в on предложении. Таким образом, эквивалент приведенного выше:

 select s.*
from School s left join
     Student st
     on st.SchoolId = s.Id left join
     AppUser au
     on au.id = st.userid and au.name = 'sashan'
where au.name is null;
 

Комментарии:

1. Я думаю, вы имели в виду au.name = ‘sashan’ в первом запросе. В противном случае это не сработает.

2. @sashang . . . Да. Спасибо.