#sql
#sql
Вопрос:
Я борюсь со следующей проблемой SQL: я должен запросить всех людей, которые не знают никого, кто посещает занятия, используя следующие таблицы:
╔═════════╦════════╦═══════════╦═════╗
║ Persons ║ ║ ║ ║
╠═════════╬════════╬═══════════╬═════╣
║ id ║ name ║ address ║ age ║
║ 1 ║ Teddy ║ Utrecht ║ 96 ║
║ 2 ║ Harun ║ Texas ║ 64 ║
║ 3 ║ Egbert ║ Rome ║ 68 ║
║ 4 ║ John ║ Amsterdam ║ 39 ║
╚═════════╩════════╩═══════════╩═════╝
╔═══════╦════════════╦════════════╗
║ Knows ║ ║ ║
╠═══════╬════════════╬════════════╣
║ id ║ personA_id ║ personB_id ║
║ 25 ║ 1 ║ 2 ║
║ 26 ║ 1 ║ 3 ║
║ 27 ║ 3 ║ 2 ║
║ 28 ║ 3 ║ 4 ║
╚═══════╩════════════╩════════════╝
╔══════════════╦═══════════╦══════════╗
║ TakesClasses ║ ║ ║
╠══════════════╬═══════════╬══════════╣
║ id ║ person_id ║ class_id ║
║ 35 ║ 1 ║ 50 ║
║ 36 ║ 1 ║ 51 ║
║ 37 ║ 1 ║ 52 ║
║ 38 ║ 1 ║ 53 ║
║ 39 ║ 2 ║ 54 ║
║ 40 ║ 2 ║ 55 ║
║ 41 ║ 2 ║ 56 ║
╚══════════════╩═══════════╩══════════╝
После долгого времени попыток выполнения разных запросов следующий запрос является наиболее близким к желаемому результату:
select distinct name
from Persons P
where P.id NOT IN
(select person_id
from TakesClasses T join Knows K
on T.person_id = K.personA_id
where class_id IS NOT NULL)
Кажется, я получаю больше результатов, чем необходимо для этого запроса (Харун, Эгберт и Джон), но я не могу найти ошибку, которую я делаю. Может кто-нибудь, пожалуйста, помочь мне?
Комментарии:
1. Не сохраняйте возраст. Большинство людей, как правило, становятся старше с каждым годом, и ваши данные скоро устареют. Вместо этого сохраните дату / год рождения.
2. Я считаю, что
(select person_id
это должно быть(select personB_id
, однако таблица knows должна быть симметричной
Ответ №1:
используйте это:
select P.id, name
from Persons P
where P.id NOT IN
(select K.personA_id
from TakeClasses T join Knows K
on T.person_id = K.personB_id
)
Комментарии:
1. К сожалению, это не решило проблему. Я все еще получаю слишком много результатов на выходе. Мне нужны люди, которые не знают никого, кто посещает занятия.
2. проверьте мой запрос, я только что отредактировал, я думаю, он работает сейчас
Ответ №2:
Этот тип проблемы предполагает not exists
:
select p.*
from persons p
where not exists (select 1
from takesclasses tc join
knows k
on tc.personB_id = k.person_id
where k.personA_id = p.id
);
Предложение correlation связывает подзапрос с внешним запросом.
Обратите внимание, что это предполагает, что knows
отношения симметричны. Итак, если A «знает» B, то B «знает», что A также находится в таблице.
Редактировать:
Я настоятельно рекомендую вам никогда не использовать NOT IN
подзапросы with. Он ведет себя не так, как ожидалось, если какие-либо значения, возвращаемые в подзапросе, являются NULL
. В этом случае, однако, вы можете сделать:
select p.*
from persons p
where p.id not in (select k.personA_id
from takesclasses tc join
knows k
on tc.personB_id = k.person_id
);
Комментарии:
1. Не было бы более эффективным использовать идентификатор пользователя непосредственно из
takesclasses
таблицы как отдельный? А затем присоединиться к лицам, чтобы получить имя? Предполагается, что количество людей, посещающих занятия, меньше, чем общее количество людей (т. Е. Кто-то не будет посещать какие-либо занятия).2. @ChatterOne .
name
, , Вtakesclasses
таблице нет, поэтому вам нужно.join
persons
Кроме того,select distinct
это дорогостоящая операция, поэтому я предпочитаю избегать ее.3. Да, именно поэтому я сказал
And then join Persons to get the name
. Но если вы хотите избежать distinct, альтернативы нет.4. Я должен использовать оператор NOT IN для этого упражнения. Однако ваше решение дает правильный результат. Спасибо за это. У вас есть предложение для этого запроса с использованием NOT IN? 🙂
5. Спасибо за помощь и объяснение 🙂