#sql
#sql
Вопрос:
select distinct S.ID, S.name
from student as S
where not exists (
(select course_id from course where dept_name = ’Biology’)
except
(select T.course_id from takes as T where S.ID = T.ID)
);
В этом запросе в моей книге говорится, что это означает
«Найти всех студентов, которые прослушали все курсы, предлагаемые на факультете биологии»
Таблица выглядит следующим образом:
студент (ID, имя, имя_департамента, tot_cred)
проходит (ID, course_id, sec_id, семестр, год, класс)
курс (course_id, название, имя_департамента, кредиты)
Я думал, что запрос не сработал, потому что в таблице student не было идентификатора курса.
Почему выполнение этого запроса такое же, как описано выше?
Комментарии:
1. Вложенный запрос получает все уроки биологии за вычетом тех, которые посещает учащийся. Слева — уроки биологии, которые ученик не посещал. Следовательно, основной запрос гласит: дайте мне всех учеников, у которых нет незанятых уроков биологии. Это
DISTINCT
излишне, так как, конечно, в таблице учащихся не должно быть повторяющихся учеников. Я должен сказать, что это заставляет меня поставить вашу книгу под сомнение. Заполнение каждого запроса ненужнымDISTINCT
— типичная проблема новичков.
Ответ №1:
Я думал, что запрос не сработал, потому что в таблице student не было идентификатора курса.
Обратите внимание, что course_id
на это никогда не ссылаются, кроме как из table course
( select course_id from course
) или table takes
( select T.course_id from takes as T
) . Запрос никогда не ссылается на student.course_id
, поэтому ваш вопрос противоречит вашему непониманию того, что делает запрос.
Запрос немного сбивает с толку, потому что он эффективно использует двойное отрицание. where not exists
Предложение более или менее гласит: «найдите всех студентов, где, если мы возьмем набор предлагаемых курсов по биологии и удалим все курсы, которые прослушал студент, результатом будет пустой набор».
-- Where the following set is empty...
where not exists (
-- All of the biology courses offered...
(select course_id from course where dept_name = ’Biology’)
-- EXCEPT those that the student has taken.
except
(select T.course_id from takes as T where S.ID = T.ID)
)
Если мы возьмем все предлагаемые курсы биологии и удалим курсы, которые прослушал студент, и результатом будет пустой набор, единственным возможным объяснением будет то, что студент прослушал все предлагаемые курсы биологии. (Кроме того: также возможно, что не предлагаются курсы биологии, и в этом случае студент все еще прошел все предлагаемые курсы биологии — это называется пустой правдой.)
Комментарии:
1. считается, что not exists возвращает истинные значения, когда набор пуст после предложения. Но я до сих пор не знаю, почему в результатах выводятся учащиеся, которые прошли все занятия по биологии.
2. @Kim Внимательно прочитайте ответ и проверьте, что делает запрос. Если идентификаторы курсов для всех классов биологии равны 1, 2 и 3, а учащийся прослушал курсы 1, 2, 3 и 4, что произойдет, если вы возьмете первый набор и удалите из него все элементы, которые также есть во втором наборе?
3. @cdhowie Я знаю, что это пустое множество, но я не знаю, как работает «не существует» и почему результаты распечатывают все курсы по биологии.
4. @Kim
NOT EXISTS
просто утверждает, что следующий set / подзапрос пуст. Поэтому, если учащийся прошел все уроки биологии,NOT EXISTS (...)
предложение оценивается как true, и оцениваемая запись учащегося включается в набор результатов. Результаты не должны распечатывать все курсы биологии, результатами должны быть все учащиеся, которые прошли все курсы биологии.5. @cdhowie Что произошло бы, если бы были студенты, которые не проходили факультет биологии, но проходили курс в другом отделе?
Ответ №2:
В запросе говорится: «На биологическом факультете не существует курса, который студент не посещал».
Второй подзапрос (после except
) получает все курсы, которые проходит студент. Они удалены из всех курсов на факультете биологии. Итак, если студент прошел все курсы на этом факультете, результатом не будет строк. В противном случае результатом будут строки, которые студент не взял.
Это select distinct
сильно вводит в заблуждение. В таблице student не должно быть дубликатов.
Я предпочитаю использовать агрегацию для этих типов запросов:
select t.student_id
from takes t join
course c
on t.course_id = c.course_id
where c.dept_name = 'Biology'
group by t.student_id
having count(distinct t.course_id) = (select count(*) from course c2 where c2.dept_name = 'Biology');
Для меня (по крайней мере) логика здесь более четко соответствует «студенты прошли все курсы на биологическом факультете».