Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
135
Добавлен:
22.03.2015
Размер:
2.28 Mб
Скачать

12. Использование оператора exists

ТЕПЕРЬ, КОГДА ВЫ ХОРОШО ОЗНАКОМЛЕНЫ С ПОДЗАПРОСАМИ, мы можем гово-

рить о некоторых специальных операторах которые всегда берут подзапро-

сы как аргументы. Вы узнаете о первом из их в этой главе. Остальные

будут описан в следующей главе.

Оператор EXISTS используется чтобы указать предикату, - производить

ли подзапросу вывод или нет. В этой главе, вы узнаете как использовать

этот оператор со стандартными и ( обычно ) соотнесенными подзапросами.

Мы будем также обсуждать специальные расмышления которые перейдут в

игру когда вы будете использовать этот оператор как относительный аг-

регат, как пустой указатель NULL, и как оператор Буля. Кроме того, вы

можете повысить ваш профессиональный уровень относительно подзапросов

исследуя их в более сложных прикладных программах чем те которые мы

видели до сих пор.

Как работает EXISTS?

EXISTS - это оператор, который производит верное или неверное значе-

ние, другими словами, выражение Буля ( см. Главу 4 для обзора этого

термина ). Это означает что он может работать автономно в предикате

или в комбинации с другими выражениями Буля использующими Булевы опе-

раторы AND, OR, и NOT. Он берет подзапрос как аргумент и оценивает его

как верный если тот производит любой вывод или как неверный если тот

не делает этого. Этим он отличается от других операторов предиката, в

которых1 он не может быть неизвестным. Например, мы можем решить, изв-

лекать ли нам некоторые данные из таблицы Заказчиков если, и только

если, один или более заказчиков в этой таблице находятсяся в San Jose

( вывод для этого запроса показывается в Рисунке 12.1 ):

SELECT cnum, cname, city

FROM Customers

WHERE EXISTS

( SELECT *

FROM Customers

WHERE city = " San Jose' );

Внутренний запрос выбирает все данные для всех заказчиков в San Jo-

se. Оператор EXISTS во внешнем предикате отмечает, что некоторый вывод

был произведен подзапросом, и поскольку выражение EXISTS было полным

предикатом, делает предикат верным. Подзапрос( не соотнесенный ) был

выполнен только один раз для всего внешнего запроса, и следовательно,

=============== SQL Execution Log ============

| |

| SELECT snum, sname, city |

| FROM Customers |

| WHERE EXISTS |

| (SELECT * |

| FROM Customers |

| WHERE city = 'San Jose'); |

| ============================================= |

| cnum cname city |

| ----- -------- ---- |

| 2001 Hoffman London |

| 2002 Giovanni Rome |

| 2003 Liu San Jose |

| 2004 Grass Berlin |

| 2006 Clemens London |

| 2008 Cisneros San Jose |

| 2007 Pereira Rome |

=============================================

Рисунок 12.1 Использование оператора EXISTS

имеет одно значение во всех случаях. Поэтому EXISTS, когда использует-

ся этим способом, делает предикат верным или неверным для всех строк

сразу, что это не так уж полезно для извлечения определенной информа-

ции.

Выбор столбцов с помощью EXISTS

В вышеупомянутом примере, EXISTS должен быть установлен так чтобы

легко выбрать один столбец, вместо того, чтобы выбирать все столбцы

используя в выборе звезду( SELECT *) В этом состоит его отличие от

подзапроса который ( как вы видели ранее в Главе 10 мог выбрать только

один столбец ) . Однако, в принципе он мало отличается при выборе

EXISTS столбцов, или когда выбираются все столбцы, потому что он прос-

то замечает - выполняется или нет вывод из подзапроса - а не использу-

ет выведенные значения.

Использование EXISTS с соотнесеннымми подзапросами

В соотнесенном подзапросе, предложение EXISTS оценивается отдельно

для каждой строки таблицы имя которой указано во внешнем запросе, точ-

но также как и другие операторы предиката, когда вы используете соот-

несенный подзапрос. Это дает возможность использовать EXISTS как вер-

ный предикат, который генерирует различные ответы для каждой строки

таблицы указанной в основном запросе. Следовательно информация из

внутреннего запроса, будет сохранена, если выведена непосредственно,

когда вы используете EXISTS таким способом. Например, мы можем вывести

продавцов которые имеют многочисленых заказчиков ( вывод для этого

запроса показывается в Рисунке 12.2 ):

SELECT DISTINCT snum

FROM Customers outer

WHERE EXISTS

( SELECT *

FROM Customers inner

WHERE inner.snum = outer.snum

AND inner.cnum < > outer.cnum );

=============== SQL Execution Log ============

| |

| SELECT DISTINCT cnum |

| FROM Customers outer |

| WHERE EXISTS |

| (SELECT * |

| FROM Customers inner |

| WHERE inner.snum = outer.snum |

| AND inner.cnum < > outer.cnum); |

| ============================================= |

| cnum |

| ----- |

| 1001 |

| 1002 |

=============================================

Рисунок 12. 2: Использование EXISTS с соотнесенным подзапросом

Для каждой строки-кандидата внешнего запроса ( представляющей заказ-

чика проверяемого в настоящее время ), внутренний запрос находит стро-

ки которые совпадают со значением поля snum ( которое имел продавец ),

но не со значением поля cnum ( сответствующего другим заказчикам ).

Если любые такие строки найдены внутренним запросом, это означает, что

имеются два разных заказчика обслуживаемых текущим продавцом ( то-есть

продавцом заказчика в текущей строке-кандидата из внешнего запроса ).

Предикат EXISTS поэтому верен для текущей строки, и номер продавца по-

ля (snum) таблицы указанной во внешнем запросе будет выведено. Если

был DISTINCT не указан, каждый из этих продавцов будет выбран один раз

для каждого заказчика к которому он назначен.

Комбинация из EXISTS и объединения

Однако для нас может быть полезнее вывести больше информации об этих

продавцах а не только их номера. Мы можем сделать это объединив табли-

цу Заказчиков с таблицей Продавцов ( вывод для запроса показывается в

Рисунке 12.3 ):

SELECT DISTINCT first.snum, sname, first.city

FROM Salespeople first, Customers second

WHERE EXISTS

( SELECT *

FROM Customers third

WHERE second.snum = third.snum

AND second.cnum < > third.cnum )

AND first.snum = second.snum;

=============== SQL Execution Log ============

| |

| SELECT DISTINCT first.snum, sname, first.city |

| FROM Salespeople first, Customers second |

| WHERE EXISTS |

| (SELECT * |

| FROM Customers third |

| WHERE second.snum = third.snum |

| AND second.cnum < > third.cnum) |

| AND first.snum = second.snum; |

| ============================================= |

| cnum cname city |

| ----- -------- ---- |

| 1001 Peel London |

| 1002 Serres San Jose |

=============================================

Рисунок 12.3: Комбинация EXISTS с обьединением

Внутренний запрос здесь - как и в предыдущем варианте, фактически

сообщает, что псевдоним был изменен. Внешний запрос - это обьединение

таблицы Продавцов с таблицей Заказчиков, наподобии того что мы видели

прежде. Новое предложение основного предиката ( AND first.snum = se-

cond.snum ) естественно оценивается на том же самом уровне что и пред-

ложение EXISTS. Это - функциональный предикат самого обьединения,

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

торое являются для них общим. Из-за Булева оператора AND, оба условия

основного предиката должны быть верны в порядке для верного предиката.

Следовательно, результаты подзапроса имеют смысл только в тех случаях

когда вторая часть запроса верна, а обьединение - выполняемо. Таким

образом комбинация объединения и подзапроса может стать очень мощным

способом обработки данных.

Использование NOT EXISTS

Предыдущий пример дал понять что EXISTS может работать в комбинации

с операторами Буля. Конечно, то что является самым простым способом

для использования и вероятно наиболее часто используется с EXISTS -

это оператор NOT. Один из способов которым мы могли бы найти всех про-

давцов только с одним заказчиком будет состоять в том, чтобы инверти-

ровать наш предыдущий пример. ( Вывод для этого запроса показывается в

Рисунке 12.4:)

SELECT DISTINCT snum

FROM Customers outer

WHERE NOT EXISTS

( SELECT *

FROM Customers inner

WHERE inner.snum = outer.snum

AND inner.cnum < > outer.cnum );

EXISTS и агрегаты

Одна вещь которую EXISTS не может сделать - взять функцию агрегата в

подзапросе. Это имеет значение. Если функция агрегата находит любые

строки для операций с ними, EXISTS верен, не взирая на то, что это -

зна-| чение функции ; если же агрегатная функция не находит никаких

строк, EXISTS неправилен.

=============== SQL Execution Log ============

| |

| SELECT DISTINCT snum |

| FROM Salespeople outer |

| WHERE NOT EXISTS |

| (SELECT * |

| FROM Customers inner |

| WHERE inner.snum = outer.snum |

| AND inner.cnum < > outer.cnum); |

| ============================================= |

| cnum |

| ----- |

| 1003 |

| 1004 |

| 1007 |

=============================================

Рисунок 12.4: Использование EXISTS с NOT

Попытка использовать агрегаты с EXISTS таким способом, вероятно по-

кажет что проблема неверно решалась от начала до конца.

Конечно, подзапрос в предикате EXISTS может также использовать один

или более из его собственных подзапросов. Они могут иметь любой из

различных типов которые мы видели ( или который мы будем видеть ). Та-

кие подзапросы, и любые другие в них, позволяют использовать агрегаты,

если нет другой причины по которой они не могут быть использованы.

Следующий раздел приводит этому пример.

В любом случае, вы можете получить тот же самый результат более лег-

ко, выбрав поле которое вы использовали в агрегатной функции, вместо

использования самой этой функции. Другими словами, предикат - EXISTS

(SELECT COUNT (DISTINCT sname) FROM Salespeople) - будет эквивалентен

- EXISTS (SELECT sname FROM Salespeople) который был позволен выше.

Более удачный пример подзапроса

Возможные прикладные программы подзапросов могут становиться многок-

ратно вкладываемыми. Вы можете вкладывать их два или более в одиночный

запрос, и даже один внутрь другого. Так как можно рассмотреть неболь-

шой кусок чтобы получить всю картину работаты этой команды, вы можете

воспользоваться способом в SQL, который может принимать различные ко-

манды из большинства других языков.

Имеется запрос который извлекает строки всех продавцов которые имеют

заказчиков с больше чем одним текущим порядком. Это не обязательно са-

мое простое решение этой проблемы, но оно предназначено скорее пока-

зать улучшеную логику SQL. Вывод этой информации связывает все три на-

ши типовых таблицы:

SELECT *

FROM Salespeople first

WHERE EXISTS

( SELECT *

FROM Customers second

WHERE first.snum = second.snum

AND 1 <

( SELECT COUNT (*)

FROM Orders

WHERE Orders.cnum =

second.cnum ));

Вывод для этого запроса показывается в Рисунке 12.5.

=============== SQL Execution Log ============

| |

| FROM Salespeople first |

| WHERE EXISTS |

| (SELECT * |

| FROM Customers second |

| WHERE first.snum = second.snum |

| AND 1 < |

| (SELECT CONT (*) |

| FROM Orders |

| WHERE Orders.cnum = second.cnum)); |

| ============================================= |

| cnum cname city comm |

| ----- -------- ---- -------- |

| 1001 Peel London 0.17 |

| 1002 Serres San Jose 0.13 |

| 1007 Rifkin Barselona 0.15 |

=============================================

Рисунок 12.5: Использование EXISTS с комплексным подзапросом

Мы могли бы разобрать вышеупомянутый запрос примерно так:

Берем каждую строку таблицы Продавцов как строку-кандидат( внешний

запрос ) и выполняем подзапросы. Для каждой строки-кандидата из внеш-

него запроса, берем в соответствие каждую строку из таблицы Заказчи-

ков( средний запрос ). Если текущая строка заказчиков не совпадает с

текущей строкой продавца( т.е. если first.snum < > second.snum ), пре-

дикат среднего запроса неправилен. Всякий раз, когда мы находим заказ-

чика в среднем запросе который совдает с продавцом во внешнем запросе,

мы должны рассматривать сам внутренний запрос чтобы определить, будет

ли наш средний предикат запроса верен. Внутренний запрос считает число

порядков текущего заказчика ( из среднего запроса ). Если это число

больший чем 1, предикат среднего запроса верен, и строки выбираются.

Это делает EXISTS предикат внешнего запроса верным для текущей строки

продавца, и означает, что по крайней мере один из текущих заказчиков

продавца имеет более чем один порядок.

Если это не кажется достаточно понятным для вас в этой точке разбора

примера, не волнуйтесь. Сложность этого примера - хороша независимо от

того, как часто будете Вы использовать ее в деловой ситуации. Основная

цель примеров такого типа состоит в том, чтобы показать вам некоторые

возможности которые могут оказаться в дальнейшем полезными. После ра-

боты со сложными ситуациями подобно этой, простые запросы которые яв-

ляются наиболее часто используемыми в SQL, покажутся Вам элементарны-

ми.

Кроме того, этот запрос, даже если он кажется удобным, довольно из-

вилистый способ извлечения информации и делает много работы. Он связы-

вает три разных таблицы чтобы дать вам эту информацию, а если таблиц

больше чем здесь указано, будет трудно получить ее напрямую (хотя это

не единственный способ, и не обязательно лучший способ в SQL). Возмож-

но вам нужно увидеть эту информацию относительно регулярной основы -

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

получил многочисленые порядки от одного заказчика. В этом случае, он

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

и снова по мере того как данные будут меняться ( лучше всего сделать

это с помощью представления, которое мы будем проходить в Главе 20 ).

РЕЗЮМЕ

EXISTS, хотя он и кажется простым, может быть одним из самых непо-

нятных операторов SQL. Однако, он облажает гибкостью и мощностью. В

этой главе, вы видели и овладели большинством возможностей которые

EXISTS дает вам. В дальнейшем, ваше понимание улучшеной логики подзап-

роса расширится значительно.

Следующим шагом будет овладение тремя другими специальными операто-

рами которые берут подзапросы как аргументы, это - ANY, ALL, и SOME.

Как вы увидете в Главе 13, это - альтернативные формулировки некоторых

вещей которые вы уже использовали, но которые в некоторых случаях, мо-

гут оказаться более предпочтительными.

Работа с SQL

1. Напишите запрос который бы использовал оператор EXISTS для извлече-

ния всех продавцов которые имеют заказчиков с оценкой 300.

2. Как бы вы решили предыдущую проблему используя обьединение ?

3. Напишите запрос использующий оператор EXISTS который выберет всех

продавцов с заказчиками размещенными в их городах которые ими не

обслуживаются.

4. Напишите запрос который извлекал бы из таблицы Заказчиков каждого

заказчика назначенного к продавцу который в данный момент имеет по

крайней мере еще одного заказчика ( кроме заказчика которого вы вы-

берете ) с порядками в таблице Порядков ( подсказка: это может быть

похоже на структуру в примере с нашим трех-уровневым подзапросом ).

Соседние файлы в папке Базы данных