Использование вложенных запросов
Вложенные запросы, которые являются проверкой на существование
Запрос возвращает данные из всех табличных строк, которые удовлетворяют его условиям. Иногда возвращается много строк, а иногда – только одна. Бывает так, что в таблице ни одна строка не удовлетворяет условиям и поэтому ни одна из них не возвращается. Перед подзапросом можно ставить предикаты EXISTS (существует) и NOT EXISTS (не существует). Такая структура, в которой сочетаются подзапрос и один из этих предикатов, сообщает, имеются ли в таблице, указанной в предложении FROM (из) подзапроса, какие-нибудь строки, соответствующие условиям предложения WHERE (где) того же подзапроса.
Подзапросы, перед которыми ставится один из предикатов EXISTS или NOT EXISTS, принципиально отличаются от тех подзапросов, о которых уже говорилось в этой главе. Во всех предыдущих случаях SQL вначале выполняет подзапрос, а затем применяет результат этой операции по отношению к замыкающему оператору. А подзапросы с предикатами EXISTS и NOT EXISTS – это коррелированные подзапросы, и выполняются они по-другому.
Коррелированный подзапрос вначале находит таблицу и строку, указанные замыкающим оператором, а затем выполняет подзапрос в той строке его таблицы, которая коррелирует (соотносится) с текущей строкой таблицы замыкающего оператора.
Подзапрос или возвращает одну, или несколько строк, или вообще не возвращает ни одной. Если он возвращает хотя бы одну строку, то предикат EXISTS является истинным и свое действие выполняет замыкающий оператор. В тех же условиях предикат NOT EXISTS является ложным, и замыкающий оператор свое действие не выполняет. После обработки строки в таблице внешнего оператора та же операция выполняется со следующей строкой. Это действие повторяется до тех пор, пока не будут обработаны все строки из таблицы, указанной замыкающим оператором.
EXISTS
Допустим, вы являетесь продавцом из Zetec Corporation и хотите позвонить контактным представителям всех калифорнийских организаций, покупающих продукцию Zetec. Попробуйте использовать следующий запрос:
SELECT * FROM CONTACT WHERE EXISTS (SELECT * FROM CUSTOMER WHERE CustStat" – 'CA' AND CONTACT.CuSl.? = CUSTOMER.CustID);
Обратите внимание на такую ссылку, как CONTACT.CuslTD. Она указывает на столбец из внешнего запроса. Этот столбец сравнивается с другим столбцом, CUSTOMER.CustID, находящемся в таблице внутреннего запроса. Для каждой строки внешнего запроса вы проверяете внутренний запрос, т.е. в предложении WHERE внутреннего запроса используется значение столбца CustID из текущей строки таблицы CONTACT. Эта таблица указана во внешнем запросе.
Столбец CustID связывает таблицу CONTACT с таблицей CUSTOMER. SQL переходит в первую строку таблицы CONTACT, затем находит строку в таблице CUSTOMER, имеющую то же значение CustID, и проверяет в этой строке значение столбца CustState. Если CUSTOMER.CustState = 'СА, то в выводимую таблицу добавляется текущая строка таблицы CONTACT. Точно так же обрабатывается и следующая запись этой таблицы. Так как запрос указывает SELECT * FROM CONTACT, то возвращаются все поля таблицы с данными контактных представителей, в том числе поля с фамилиями и телефонными номерами представителей.
NОТ EXISTS
В предыдущем примере продавец из Zetec хотел узнать имена и телефонные номера представителей для контакта из всех калифорнийских организаций, покупающих продукцию его компании. Предположим, что другой продавец работает со всеми остальными штатами, кроме Калифорнии. Данные о контактных представителях из других штатов можно получить с помощью запроса, похожего на предыдущий, но с предикатом NOT EXISTS:
SELECT * FROM CONTACT WHERE NOT EXISTS (SELECT * FROM CUSTOMER WHERE CustState = 'CA' AND CONTACT.CustID = CUSTOMER.CustID);
В выводимую таблицу добавляются только те строки из таблицы CONTACT, для каждой из которых подзапрос не возвращает ни одной строки.