Добро пожаловать!
Здесь вы можете найти ответ на интересующий вас вопрос в отрасли сайтостроения, познакомится ближе с web технологиями и web стандартами.

SQL

Глава 13. Использование операторов ANY, ALL и SOME

Теперь, когда вы овладели оператором EXISTS, вы узнаете о трёх специальных операторах, ориентированных на подзапросы. (Фактически имеются только два, так как ANY и SOME – одно и то же.) Если вы поймёте работу этих операторов, вы будете понимать все типы подзапросов предиката, используемых в SQL. Кроме того, вам будут представлены различные способы того, как данный запрос может быть сформирован с использованием различных типов подзапросов предиката, и вы поймёте преимущества и недостатки каждого из этих подходов.

ANY, ALL и SOME напоминают EXISTS, принимая подзапросы как аргументы; однако они отличаются от EXISTS тем, что используются совместно с реляционными операциями. В этом отношении они напоминают оператор IN, когда тот используется с подзапросами: они берут все значения, выведенные подзапросом и обрабатывают их как модуль. Однако, в отличие от IN, они могут использоваться только с подзапросами.

Специальные операторы ANY или SOME

Операторы SOME и ANY взаимозаменяемы везде, и там, где мы используем ANY, SOME будет работать точно так же. Различие в терминологии состоит в том, чтобы позволить людям использовать тот термин, который является однозначным. Это может создать проблему, поскольку, как мы это увидим, наша интуиция может иногда вводить в заблуждение.

Вот новый способ нахождения продавцов с заказчиками, размещенными в их городах (вывод для этого запроса показан на Рисунке 13.1):

SELECT *
  FROM Salespeople
  WHERE city = ANY
    (SELECT city FROM Customers);

Оператор ANY берёт все значения, выведенные подзапросом, (для этого случая это все значения city в таблице Заказчиков), и оценивает их как верные, если любое (ANY) из них равняется значению города текущей строки внешнего запроса.

               
   ===============  SQL Execution Log ============
  | SELECT *                                      |
  | FROM  Salespeople                             |
  | WHERE city = ANY                              |
  | (SELECT  city                                 |
  | FROM Customers);                              |
  | ============================================= |
  |   cnum     cname     city         comm        |
  |  -----    --------   ----       --------      |
  |   1001    Peel       London         0.12      |
  |   1002    Serres     San Jose       0.13      |
  |   1004    Motika     London         0.11      |
   ===============================================

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

Это означает, что подзапрос должен выбирать значения такого же типа, что и те, которые сравниваются в основном предикате. В этом его отличие от EXISTS, который просто определяет, производит ли подзапрос результаты или нет, и фактически не использует эти результаты.

Использование IN или EXISTS вместо ANY

Мы можем также использовать оператор IN для создания запроса, аналогичного предыдущему:

SELECT *
   FROM Salespeople
   WHERE city IN
      (SELECT city
          FROM Customers);

Этот запрос даст вывод, показанный на Рисунке 13.2.

   ===============  SQL Execution Log ============
  | SELECT *                                      |
  | FROM  Salespeople                             |
  | WHERE city IN                                 |
  | (SELECT  city                                 |
  | FROM Customers);                              |
  | ============================================= |
  |   cnum     cname     city         comm        |
  |  -----    --------   ----       --------      |
  |   1001    Peel       London         0.12      |
  |   1002    Serres     San Jose       0.13      |
  |   1004    Motika     London         0.11      |
   ===============================================
   
   Рисунок 13.2 Использование IN в качестве альтернативы ANY

Однако ANY может применяться не только с равенствами, но и с другими операторами отношения. Тем самым его возможности шире, чем у IN. Например, можно найти всех продавцов, у которых есть покупатели, чьи имена следуют за именем продавца в алфавитном порядке (вывод показан на Рисунке 13.3).

             
SELECT *
   FROM Salespeople
   WHERE sname < ANY
      (SELECT cname
          FROM Customers);

   ===============  SQL Execution Log ============
  | SELECT *                                      |
  | FROM  Salespeople                             |
  | WHERE sname < ANY                             |
  | (SELECT  cname                                |
  | FROM Customers);                              |
  | ============================================= |
  |   cnum     cname     city         comm        |
  |  -----    --------   ----       --------      |
  |   1001    Peel       London         0.12      |
  |   1004    Motika     London         0.11      |
  |   1003    Axelrod    New York       0.10      |
   ===============================================

   Рисунок 13.3 Использование оператора ANY с операцией "меньше" (<)

Здесь выбраны строки всех продавцов, за исключением Serres и Rifkin. Для них не нашлось покупателей, имена которых были бы следующими в алфавитном порядке. Обратите внимание, что этот запрос по существу эквивалентен представленному ниже запросу с EXISTS, результат которого показан на рисунке 13.4.

             
SELECT *
   FROM Salespeople outer
   WHERE EXISTS
      (SELECT *
          FROM Customers inner
          WHERE outer.sname < inner.cname);

   ===============  SQL Execution Log ============
  | SELECT *                                      |
  | FROM  Salespeople outer                       |
  | WHERE EXISTS                                  |
  | (SELECT *                                     |
  | FROM Customers inner                          |
  | WHERE outer.sname < inner.cname);             |
  | ============================================= |
  |   cnum     cname     city         comm        |
  |  -----    --------   ----       --------      |
  |   1001    Peel       London         0.12      |
  |   1004    Motika     London         0.11      |
  |   1003    Axelrod    New York       0.10      |
   ===============================================

   Рисунок 13.4 Использование EXISTS в качестве альтернативы 
               оператору ANY

Любой запрос, который может быть сформулирован с ANY (или, как мы увидим, с ALL), можно также составить с помощью EXISTS, хотя обратное неверно. Строго говоря, вариант с EXISTS не абсолютно идентичен вариантам с ANY или с ALL из-за различия в обработке пустых (NULL) значений (что будет обсуждаться позже в этой главе). Тем не менее, с технической точки зрения, вы могли бы делать это без ANY и ALL, если проявите находчивость в использовании EXISTSIS NULL).

Большинство пользователей, однако, находят ANY и ALL более удобными в использовании, чем EXISTS, который требует соотнесённых подзапросов. Кроме того, в зависимости от реализации, ANY и ALL могут, по крайней мере в теории, быть более эффективными, чем EXISTS. Подзапросы ANY или ALL могут выполняться один раз и иметь вывод, используемый, чтобы определять предикат для каждой строки основного запроса. EXISTS, с другой стороны, берёт соотнесенный подзапрос, который требует, чтобы весь подзапрос повторно выполнялся для каждой строки основного запроса. SQL пытается найти наиболее эффективный способ выполнения любой команды и может попробовать преобразовать менее эффективную формулу запроса в более эффективную (но вы не можете всегда рассчитывать на получение самой эффективной формулировки).

Основная причина для формулировки EXISTS как альтернативы ANY и ALL в том, что ANY и ALL могут быть несколько неоднозначны из-за способа использования этого термина в английском языке, как вы это скоро увидите. С приходом понимания различия способов формулирования данного запроса, вы сможете поработать над процедурами, которые сейчас кажутся вам трудными или неудобными.

Как ANY может стать неоднозначным?

Как говорилось выше, ANY не полностью однозначен. Если мы создаём запрос, чтобы выбрать заказчиков, которые имеют больший рейтинг, чем любой заказчик в Риме, мы можем получить вывод, который несколько отличается от того, что мы ожидали (как показано в Рисунке 13.5):

SELECT *
   FROM Customers
   WHERE rating > ANY
      (SELECT rating
          FROM Customers
          WHERE city = 'Rome');

В английском языке фраза "больше чем любой рейтинг в Риме" обычно означает, что нам нужен рейтинг, который больше каждого из рейтингов покупателей, живущих в этом городе. Однако в SQL слово ANY трактуется по-другому. Результатом оператора будет TRUE, если подзапрос найдет хотя бы одно значение, которое удовлетворяет условию (или несколько таких значений).

               
   ===============  SQL Execution Log ============
  |                                               |
  | SELECT *                                      |
  | FROM  Customers                               |
  | WHERE rating > ANY                            |
  | (SELECT rating                                |
  | FROM Customers                                |
  | WHERE city = 'Rome');                         |
  | ============================================= |
  |   cnum     cname     city     rating   snum   |
  |  -----    --------   ----     ------  ------  |
  |   2002    Giovanni   Rome        200    1003  |
  |   2003    Liu        San Jose    200    1002  |
  |   2004    Grass      Berlin      300    1002  |
  |   2008    Cisneros   San Jose    300    1007  |
   ===============================================

   Рисунок 13.5 Как операция "больше" (>) интерпретируется ANY

Если бы ANY выполнялся в соответствии с правилами английского языка, то лишь покупатели с рейтингом 300 превзошли бы Giovanni, который живет в Риме и имеет рейтинг 200. Однако подзапрос ANY нашел в Риме покупателя по имени Pereira с рейтингом 100. Поскольку все покупатели с рейтингом 200 превосходят его, они выбираются, несмотря на присутствие в Риме другого покупателя (Giovanni), чей рейтинг равен 200. Поскольку подзапрос нашел как минимум одно значение, обращающее предикат в TRUE для этих строк, они и были выбраны.

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

SELECT *
   FROM Orders
   WHERE amt > ANY
      (SELECT amt
          FROM Orders
          WHERE odate = '10/06/1990');

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

               
   ===============  SQL Execution Log ==============
  |                                                 |
  | SELECT *                                        |
  | FROM  Orders                                    |
  | WHERE amt > ANY                                 |
  | (SELECT amt                                     |
  | FROM Orders                                     |
  | WHERE odate = '10/06/1990');                    |
  | =============================================== |
  |   onum       amt      odate      cnum     snum  |
  |  -----    --------  ----------  -----   ------  |
  |   3002     1900.10  10/03/1990   2007     1004  |
  |   3005     5160.45  10/03/1990   2003     1002  |
  |   3009     1713.23  10/04/1990   2002     1003  |
  |   3008     4723.00  10/05/1990   2006     1001  |
  |   3011     9891.88  10/06/1990   2006     1001  |
   =================================================

   Рисунок 13.6 Выбрано значение, большее чем любое (ANY) на 6-е октября

Несмотря на то, что 6 октября был сделан самый крупный заказ во всей таблице (9891.88), выводятся также строки, имеющие большие значения amt, чем еще одна строка от 6 октября с amt=1309.95. Если вместо оператора > использовать >=, эта строка тоже будет выбрана, поскольку она равна самой себе.

Можно применять ANY и с другими элементами SQL, например с соединениями. Следующий запрос ищет все заказы, которые стоят меньше любого заказа покупателя из San Jose (вывод показан на Рисунке 13.7):

SELECT *
   FROM Orders
   WHERE amt < ANY
      (SELECT amt
          FROM Orders a, Customers b
          WHERE a.cnum = b.cnum
          AND b.city = 'San Jose');
		  
   ===============  SQL Execution Log ==============
  |                                                 |
  | WHERE amt < ANY                                 |
  | (SELECT amt                                     |
  | FROM Orders a, Customers b                      |
  | WHERE a.cnum = b.cnum                           |
  | AND b.city = 'San Jose');                       |
  | =============================================== |
  |   onum       amt      odate      cnum     snum  |
  |  -----    --------  ----------  -----   ------  |
  |   3001       18.69  10/03/1990   2008     1007  |
  |   3003      767.10  10/03/1990   2001     1001  |
  |   3002     1900.10  10/03/1990   2007     1004  |
  |   3006     1098.10  10/03/1990   2008     1007  |
  |   3009     1713.23  10/04/1990   2002     1003  |
  |   3007       75.10  10/04/1990   2004     1002  |
  |   3008     4723.00  10/05/1990   2006     1001  |
  |   3010     1309.88  10/06/1990   2004     1002  |
   =================================================

   Рисунок 13.7 Использование ANY с объединением

Самый маленький заказ в таблице был сделан покупателем из San Jose, но ему же принадлежит и второй по величине заказ, поэтому выбраны почти все строки. При использовании ANY нужно помнить, что "< ANY" означает "меньше максимального из выбранных значений", а "> ANY" — "больше минимального из выбранных значений". Фактически, приведенный выше запрос можно сформулировать следующим образом (вывод показан на Рисунке 13.8):

SELECT *
   FROM Orders
   WHERE amt < MAX(amt)
      (SELECT amt
          FROM Orders a, Customers b
          WHERE a.cnum = b.cnum
          AND b.city = 'San Jose');

   ===============  SQL Execution Log ==============
  |                                                 |
  | WHERE amt <                                     |
  | (SELECT MAX (amt)                               |
  | FROM Orders a, Customers b                      |
  | WHERE a.cnum = b.cnum                           |
  | AND b.city = 'San Jose');                       |
  | =============================================== |
  |   onum       amt      odate      cnum     snum  |
  |  -----    --------  ----------  -----   ------  |
  |   3002     1900.10  10/03/1990   2007     1004  |
  |   3005     5160.45  10/03/1990   2003     1002  |
  |   3009     1713.23  10/04/1990   2002     1003  |
  |   3008     4723.00  10/05/1990   2006     1001  |
  |   3011     9891.88  10/06/1990   2006     1001  |
   =================================================

   Рисунок 13.8 Использование агрегатной функции вместо ANY

Специальный оператор ALL

С помощью ALL, предикат будет верным, если каждое значение, выбранное подзапросом, удовлетворяет условию в предикате внешнего запроса. Если мы хотим пересмотреть наш предыдущий пример, чтобы вывести только тех заказчиков, чьи оценки фактически выше, чем у каждого заказчика в Париже, мы можем ввести следующее, чтобы получить вывод, показанный в Рисунке 13.9:

             
SELECT *
   FROM Customers
   WHERE rating > ALL
      (SELECT rating
          FROM Customers
          WHERE city = 'Rome'):

   ===============  SQL Execution Log ============
  |                                               |
  | SELECT *                                      |
  | FROM  Customers                               |
  | WHERE rating > ALL                            |
  | (SELECT rating                                |
  | FROM Customers                                |
  | WHERE city = 'Rome');                         |
  | ============================================= |
  |   cnum     cname     city     rating   snum   |
  |  -----    --------   ----     ------  ------  |
  |   2004    Grass      Berlin      300    1002  |
  |   2008    Cisneros   San Jose    300    1007  |
   ===============================================

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

Этот оператор проверяет значения рейтинга всех заказчиков в Риме. Затем он находит заказчиков с оценкой, большей, чем у любого из заказчиков в Риме. Самая высокая оценка в Риме — у Giovanni (200). Следовательно, выбираются только значения выше 200.

Как и в случае с ANY, мы можем использовать EXISTS для создания альтернативной формулировки такого же запроса (вывод показан на Рисунке 13.10):

          
SELECT *
   FROM Customers outer
   WHERE NOT EXISTS
      (SELECT *
          FROM Customers inner
          WHERE outer.rating <= inner.rating
          AND inner.city = 'Rome');

   ===============  SQL Execution Log ============
  |                                               |
  | SELECT *                                      |
  | FROM  Customers outer                         |
  | WHERE NOT EXISTS                              |
  | (SELECT *                                     |
  | FROM Customers inner                          |
  | WHERE outer rating <= inner.rating            |
  | AND inner.city = 'Rome');                     |
  | ============================================= |
  |   cnum     cname     city     rating   snum   |
  |  -----    --------   ----     ------  ------  |
  |   2004    Grass      Berlin      300    1002  |
  |   2008    Cisneros   San Jose    300    1007  |
   ===============================================

   Рисунок 13.10 Использование EXISTS в качестве альтернативы ALL

Равенства и неравенства

ALL чаще используется с неравенствами, нежели с равенствами, так как значение может быть "равным для всех" результатом подзапроса, только если все результаты фактически идентичны. Посмотрите на следующий запрос:

SELECT *
   FROM Customers
   WHERE rating = ALL
      (SELECT rating
          FROM Customers
          WHERE city = 'San Jose');

Эта команда допустима, но с этими данными мы не получим никакого вывода. Только в единственном случае вывод будет выдан этим запросом — если все значения оценки в San Jose окажутся идентичными. В этом случае можно сказать следующее:

SELECT *
   FROM Customers
   WHERE rating =
      (SELECT DISTINCT rating
          FROM Customers
          WHERE city = 'San Jose');

Основное различие в том, что эта последняя команда должна потерпеть неудачу, если подзапрос выведет много значений, в то время как вариант с ALL просто не даст никакого вывода. В общем, не самая удачная идея — использовать запросы, которые работают только в определённых ситуациях, подобно этой. Так как ваша БД будет постоянно меняться, это неудачный способ узнать о её содержании. Однако ALL может более эффективно использоваться с неравенствами, то есть с операцией <>. Но учтите, что сказанное в SQL о значении, которое не равняется всем результатам подзапроса, будет отличаться от того же, но сказанного с учётом грамматики английского языка. Очевидно, если подзапрос возвращает много различных значений, как это обычно бывает, ни одно отдельное значение не может быть равно им всем в обычном смысле. В SQL выражение <> ALL в действительности означает "не равен любому" из результатов подзапроса. Другими словами, предикат верен, если данное значение не найдено среди результатов подзапроса. Следовательно, наш предыдущий пример противоположен по смыслу этому примеру (с выводом, показанным на Рисунке 13.11):

          
SELECT *
   FROM Customers
   WHERE rating <> ALL
      (SELECT rating
          FROM Customers
          WHERE city = 'San Jose');

   ===============  SQL Execution Log ============
  |                                               |
  | SELECT *                                      |
  | FROM  Customers                               |
  | WHERE rating <> ALL                           |
  | (SELECT rating                                |
  | FROM Customers                                |
  | WHERE city = 'San Jose');                     |
  | ============================================= |
  |   cnum     cname     city     rating   snum   |
  |  -----    --------   ----     ------  ------  |
  |   2001    Hoffman    London      100    1001  |
  |   2006    Clemens    London      100    1001  |
  |   2007    Pereira    Rome        100    1004  |
   ===============================================

  Рисунок 13.11 Использование ALL с <>

Вышеупомянутый подзапрос выбирает все рейтинги для города San Jose. Он выводит набор из двух значений: 200 (для Liu) и 300 (для Cisneros). Затем основной запрос выбирает все строки с рейтингом, не совпадающей ни с одной из них, другими словами — все строки с оценкой 100. Вы можете сформулировать тот же самый запрос с помощью операторов NOT IN:

SELECT *
   FROM Customers
   WHERE rating NOT IN
      (SELECT rating
          FROM Customers
          WHERE city = 'San Jose');

Вы могли бы также использовать оператор ANY:

SELECT *
   FROM Customers
   WHERE NOT rating = ANY
      (SELECT rating
          FROM Customers
          WHERE city = 'San Jose');

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

Правильное понимание ANY и ALL

В SQL, сказать, что значение больше (или меньше), чем любое (ANY) из набора значений — то же самое, что сказать, что оно больше (или меньше), чем любое отдельное из этих значений. И наоборот, сказать, что значение не равно всему (ALL) набору значений, это то же, что сказать, что нет такого значения в наборе которому оно равно.

Как ANY, ALL и EXIST поступают с отсутствующими и неизвестными данными?

Как было сказано, имеются некоторые различия между EXISTS и операторами, представленными в этой главе, в том, как они обрабатывают оператор NULL. ANY и ALL также отличаются друг от друга тем, как они реагируют, если подзапрос не произвел никаких значений, чтобы использовать их в сравнении. Эти различия могут привести к непредвиденным результатам в ваших запросах, если вы не будете их учитывать.

Когда подзапрос возвращается пустым?

Одно существенное различие между ALL и ANY — способ действия в ситуации, когда подзапрос не возвращает никаких значений. В принципе всякий раз, когда допустимый подзапрос не в состоянии сделать вывод, ALL автоматически правилен, а ANY автоматически неправилен. Это означает, что следующий запрос

SELECT *
   FROM Customers
   WHERE rating > ANY
      (SELECT rating
          FROM Customers
          WHERE city = 'Boston');

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

SELECT *
   FROM Customers
   WHERE rating > ALL
      (SELECT rating
          FROM Customers
          WHERE city = 'Boston');

выведет всю таблицу Заказчиков. Когда нет никаких заказчиков в Boston, естественно, ни одно из этих сравнений не имеет значения.

ANY И ALL вместо EXISTS с NULL

Значения NULL также имеют некоторые проблемы с операторами вроде этих. Когда SQL сравнивает два значения в предикате, одно из которых пустое (NULL), то результат неизвестен (смотрите Главу 5). Неизвестный предикат подобен неверному и является причиной того, что строка не выбирается, но работать он будет иначе в некоторых похожих запросах, в зависимости от того, используют они ALL или ANY вместо EXISTS.

Рассмотрим наш предыдущий пример:

             
SELECT *
   FROM Customers
   WHERE rating > ANY
      (SELECT rating
          FROM Customers
          WHERE city = 'Rome');

И ещё один пример:

          
SELECT *
   FROM Customers outer
   WHERE EXISTS
      (SELECT *
          FROM Customers inner
          WHERE outer.rating > inner.rating
          AND inner.city = 'Rome');

В общем, эти два запроса будут вести себя одинаково. Но предположим, что появилось пустое (NULL) значение в столбце rating таблицы Заказчиков:

   CNUM        CNAME         CITY        RATING       SNUM
   2003         Liu          SanJose      NULL        1002

В варианте с ANY, где оценка Liu выбрана основным запросом, значение NULL делает предикат неизвестным, а строка Liu не выбирается для вывода. Однако в варианте с NOT EXISTS, когда эта строка выбрана основным запросом, значение NULL используется в предикате подзапроса, делая его неизвестным в каждом случае. Это означает что подзапрос не будет производить никаких значений и EXISTS будет неправилен. Это, естественно, делает оператор NOT EXISTS верным. Следовательно, строка Liu будет выбрана для вывода. Это основное расхождение, в отличие от других типов предикатов, где значение EXISTS, независимо от того, верно оно или нет, всегда неизвестно. Всё это является аргументом в пользу варианта формулировки с ANY. Мы не считаем, что значение NULL является выше, чем допустимое значение. Более того, результат будет тот же, если мы будем проверять для более низкого значения.

Использование COUNT вместо EXISTS

Подчеркнём, что все формулировки с ANY и ALL могут быть в точности выполнены с EXISTS, в то время как обратное будет неверно. Хотя в этом случае также верно и то, что подзапросы EXISTS и NOT EXISTS могут проколоть при выполнении тех же самых подзапросов с COUNT(*) в предложении SELECT подзапроса. Если больше чем ноль строк в выводе будет подсчитано, это эквивалентно EXISTS; в противном случае это работает так же, как NOT EXISTS. Следующее является этому примером (вывод показан на Рисунке 13.12):

              
SELECT *
   FROM Customers outer
   WHERE NOT EXISTS
      (SELECT *
          FROM Customers inner
          WHERE outer.rating <= inner.rating
          AND inner.city = 'Rome');

   ===============  SQL Execution Log ============
  |                                               |
  | SELECT *                                      |
  | FROM  Customers outer                         |
  | WHERE NOT EXISTS                              |
  | (SELECT *                                     |
  | FROM Customers inner                          |
  | WHERE outer.rating <= inner.rating            |
  | AND inner.city = 'Rome');                     |
  | ============================================= |
  |   cnum     cname     city     rating   snum   |
  |  -----    --------   ----     ------  ------  |
  |   2004    Grass      Berlin      300    1002  |
  |   2008    Cisneros   San Jose    300    1007  |
   ===============================================

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

Это должно также быть выполнено как

            
SELECT *
   FROM Customers outer
   WHERE 1 >
      (SELECT COUNT (*)
          FROM Customers inner
          WHERE outer.rating <= inner.rating
          AND inner.city = 'Rome');

Вывод к этому запросу показан на Рисунке 13.13. Теперь вы начинаете понимать, сколько способов имеется в SQL. Если это всё кажется несколько сложным на данном этапе, нет причин волноваться. Вы обучаетесь, чтобы использовать ту технику, которая больше отвечает вашим требованиям и наиболее понятна для вас. Начиная с этого места, мы хотим показать вам большое количество возможностей, чтобы вы могли найти ваш собственный стиль.

               
   ===============  SQL Execution Log ============
  |                                               |
  | SELECT *                                      |
  | FROM  Customers outer                         |
  | WHERE 1 >                                     |
  | (SELECT COUNT (*)                             |
  | FROM Customers inner                          |
  | WHERE outer.rating <= inner.rating            |
  | AND inner.city = 'Rome');                     |
  | ============================================= |
  |   cnum     cname     city     rating   snum   |
  |  -----    --------   ----     ------  ------  |
  |   2004    Grass      Berlin      300    1002  |
  |   2008    Cisneros   San Jose    300    1007  |
   ===============================================

   Рисунок 13.13 Использование COUNT вместо EXISTS

Резюме

Итак, вы узнали много нового в этой главе. Подзапросы — непростая тема, и мы потратили много времени, чтобы показать их варианты и неоднозначность. То, чему вы теперь научились, вещи достаточно глубокие. Вы знаете несколько технических решений одной проблемы, и поэтому можете выбрать то, которое больше подходит для ваших целей. Кроме того, вы поняли, как различные формулировки будут обрабатывать пустые значения (NULL) и ошибки.

Теперь, когда вы полностью изучили запросы — наиболее важный, и, вероятно, наиболее сложный аспект SQL — другой материал будет относительно прост для понимания.

У нас есть ещё одна глава о запросах, которая покажет вам, как объединить вывод любого количества запросов в единое тело с помощью формирования объединения нескольких запросов, используя оператор UNION.

Работа с SQL

  1. Напишите запрос, который выбирал бы всех заказчиков, чьи оценки равны или больше, чем любая (ANY) оценка заказчика Serres.
  2. Что будет выведено вышеупомянутой командой?
  3. Напишите запрос, использующий ANY или ALL, который находил бы всех продавцов, которые не имеют никаких заказчиков, живущих в их городе.
  4. Напишите запрос, который выбирал бы все заказы с суммой, больше, чем любая (в обычном смысле) для заказчиков в Лондоне.
  5. Напишите предыдущий запрос с использованием MAX.

(См. ответы в Приложении A.)