Для работы с подзапросами есть несколько очень удобных операторов, которые мы еще не рассматривали. Первым оператором, с которым мы еще не работали, будет EXISTS. Этому оператору в качестве параметра передается запрос и если запрос возвращает хотя бы одну строку, то оператор возвращает истинное значение иначе ложь. В зависимости от этого, внешний запрос выносит решение, включать текущую запись в результирующий набор или нет.
Операторы ANY, SOME и ALL также позволяют расширить возможности подзапросов и работы с ними. Операторы ANY и SOME чем-то похожи на IN, с которым мы работали. Но чтобы увидеть все преимущества, необходимо посмотреть реальный запрос и разобрать его результат.
Теперь познакомимся поближе со всеми этими операторами и увидим их работу на практике.
По идее, оператор EXISTS нужно было рассмотреть намного раньше, потому что он достаточно прост. Но я решил повременить, потому что его удобно использовать совместно с подзапросами. Например, давайте выведем на экран всех работников, у которых есть хоть какой-либо телефон. Как это сделать? Можно связать две таблицы и те записи работников и получить связанные строки. Это и будут работники, у которых есть номера телефонов.
Но не всегда так легко решить проблему. Иногда удобнее и проще воспользоваться оператором EXISTS, который возвращает истинное значение, если запрос, который указан в скобках после оператора, возвращает хотя бы одну строку. Например:
SELECT * FROM tbPeoples pl WHERE EXISTS (SELECT * FROM tbPhoneNumbers pn WHERE pl.idPeoples=pn.idPeoples)
В этом запросе мы запрашиваем все записи из таблицы tbPeoples, в качестве условия происходит проверка EXISTS. Для каждой записи работника выполняется запрос, который указан в скобках после EXISTS:
SELECT * FROM tbPhoneNumbers pn WHERE pl.idPeoples=pn.idPeoples
Здесь мы связываем с внешним запросом таблицу tbPhoneNumbers. Если этот запрос найдет хотя бы один телефон для текущего работника, то EXISTS возвращает истину и строка попадает в результат.
Чтобы отобразить все записи работников, которые не имеют телефонов, достаточно перед EXISTS поставить оператор NOT:
SELECT * FROM tbPeoples pl WHERE NOT EXISTS (SELECT * FROM tbPhoneNumbers pn WHERE pl.idPeoples=pn.idPeoples)
Благодаря оператору NOT происходит обратная операция, т.е. в результат попадает та строка, для которой запрос после EXISTS не вернул ни одной строки.
Операторы ANY и SOME абсолютно одинаковы. Оба они дают один и тот же результат, поэтому вы можете использовать тот, который больше нравиться. Мне больше нравиться ANY, потому что это слово короче аж на одну букву :).
В начале этой главы я намекнул на то, что ANY очень похож на IN. Давайте посмотрим на следующий запрос с оператором IN:
SELECT * FROM tbPhoneNumbers WHERE idPhoneType IN ( SELECT idPhoneType FROM tbPhoneType WHERE vcTypeName in ('Сотовый рабочий', 'Сотовый домашний') )
В данном примере используется IN для сравнения поля "idPhoneType" с одним из значений, возвращаемым подзапросом. То же самое можно сделать с помощью оператора ANY, только используется этот немного по-другому:
SELECT * FROM tbPhoneNumbers WHERE idPhoneType > ANY ( SELECT idPhoneType FROM tbPhoneType WHERE vcTypeName in ('Сотовый рабочий', 'Сотовый домашний') )
Так в чем же разница? А она кроется в том, как читается этот запрос. Попробуем проговорить его: «Выбрать все записи из таблицы tbPhoneNumbers, где поле "idPhoneType" равно любому из значений, полученных подзапросом». Разница в том, что IN работает также как и = ANY, но с оператором ANY можно использовать любые другие операторы сравнения: = или <> или != или > или >= или !> или < или <= или !<. Например, следующий запрос выбирает все телефоны из таблицы tbPhoneNumbers, у которых "idPhoneType" больше любого из значений, полученных в подзапросе:
SELECT * FROM tbPhoneNumbers WHERE idPhoneType > ANY ( SELECT idPhoneType FROM tbPhoneType WHERE vcTypeName in ('Сотовый рабочий', 'Сотовый домашний') )
Если ANY сравнивает с любым из значений и если "idPhoneType" будет больше хотя бы одного, то запись попадает в результат. Если ANY заменить на оператор ALL, то строка попадет в результирующий набор, только если она будет больше всех значений, которые возвращает подзапрос:
SELECT * FROM tbPhoneNumbers WHERE idPhoneType > ALL ( SELECT idPhoneType FROM tbPhoneType WHERE vcTypeName in ('Сотовый рабочий', 'Сотовый домашний') )
Если ANY и SOME требуют, чтобы условие выполнилось хотя бы с одной строкой результата подзапроса, то ALL будет требовать, чтобы условие выполнилось ко всем строкам.