Еще раз о подзапросах

Заметим, что в общем случае запрос возвращает множество значений. Поэтому использование подзапроса в предложении WHERE без предикатов EXISTS, IN, ALL и ANY, которые дают булево значение, может привести к ошибке времени выполнения запроса.

Пример 5.8.3

Найти модели и цены ПК, стоимость которых превышает минимальную стоимость ноутбука:
SELECT DISTINCT model, price
FROM PC
WHERE price > (SELECT MIN(price)
               FROM Laptop
              );
🚫
[[ error ]]
[[ column ]]
[[ value ]]

Этот запрос вполне корректен, так как скалярное значение price сравнивается с подзапросом, который возвращает единственное значение. В результате получим четыре модели ПК:

modelprice
1121850
1233950
1233970
1233980

Однако, если в ответ на вопрос «найти модели и цены ПК, стоимость которых совпадает со стоимостью ноутбуков» написать следующий запрос

SELECT DISTINCT model, price
FROM PC
WHERE price = (SELECT price
               FROM Laptop
              );
🚫
[[ error ]]
[[ column ]]
[[ value ]]

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

Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.

(«Подзапрос вернул более одного значения. Это не допускается в тех случаях, когда подзапрос следует после =, !=, <, <=, >, >= или когда подзапрос используется в качестве выражения».)

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

Подзапросы, в свою очередь, также могут содержать вложенные запросы.

С другой стороны, подзапрос, возвращающий множество строк и содержащий несколько столбцов, вполне естественно может использоваться в предложении FROM. Это, например, позволяет ограничить набор столбцов и/или строк при выполнении операции соединения таблиц.

Пример 5.8.4

Вывести производителя, тип, модель и частоту процессора для ноутбуков, частота процессора которых превышает 600 МГц.

Этот запрос может быть сформулирован, например, следующим образом:

SELECT prod.maker, lap.*
FROM (SELECT 'laptop' AS type, model, speed
      FROM laptop
      WHERE speed > 600
     ) AS lap 
    INNER JOIN
    (SELECT maker, model
    FROM product
    ) AS prod ON lap.model = prod.model;
🚫
[[ error ]]
[[ column ]]
[[ value ]]

В результате получим:

makertypemodelspeed
Blaptop1750750
Alaptop1752750

Наконец, подзапросы могут присутствовать в предложении SELECT. Это иногда позволяет весьма компактно сформулировать запрос.

Пример 5.8.5

Найти разницу между средними значениями цены ноутбуков и ПК, то есть насколько в среднем ноутбук стоит дороже, чем ПК.

Здесь вообще можно обойтись одним предложением SELECT в основном запросе:

SELECT (SELECT AVG(price)
        FROM Laptop
       ) 
    -
        (SELECT AVG(price)
         FROM PC
        ) AS dif_price;
🚫
[[ error ]]
[[ column ]]
[[ value ]]

В результате получим

dif_price
328.333

Рекомендуемые упражнения: 10, 18, 24, 25, 262843, 56, 57, 60, 61, 62, 63, 72, 75, 80, 87, 88, 92, 96, 99, 110, 111, 112, 113, 118, 127, 129