Подзапросы в SELECT
В выражении SELECT мы можем вводить подзапросы четырьмя способами:
- В условии в выражении WHERE
- В условии в выражении HAVING
- В качестве таблицы для выборки в выражении FROM
- В качестве спецификации столбца в выражении SELECT
Рассмотрим некоторые из этих случаев. Например, получим все товары, у которых цена выше средней:
| 1 2 | SELECT * FROM ProductsWHERE Price > (SELECT AVG(Price) FROM Products) |
Чтобы получить нужные товары, нам вначале надо выполнить подзапрос на получение средней цены товара: SELECT AVG(Price) FROM Products.
Оператор IN
Нередко подзапросы применяются вместе с оператором IN, который выбирает из набора значений. И подзапрос как раз может предоставить требуемый набор значений. Например, выберем все товары из таблицы Products, на которые есть заказы в таблице Orders:
| 1 2 | SELECT * FROM ProductsWHERE Id IN (SELECT ProductId FROM Orders) |
То есть подзапрос в данном случае выбирает все идентификаторы товаров из Orders, затем по этим идентификаторам извлекаютя товары из Products.
Добавив оператор NOT, мы можем выбрать те товары, на которые нет заказов в таблице Orders:
| 1 2 | SELECT * FROM ProductsWHERE Id NOT IN (SELECT ProductId FROM Orders) |
Стоит отметить, что это не самый эффективный способ для извлечения связанных данных из других таблиц, так как для сведения данных из разных таблиц можно использовать оператор JOIN, который рассматривается в следующей главе.
Получение набора значений
При использовании в операторах сравнения подзапросы должны возвращать одно скалярное значение. Но иногда возникает необходимость получить набор значений. Чтобы при использовании в операторах сравнения подзапрос мог возвращать набор значений, перед ним необходимо использовать один из операторов: ALL, SOME или ANY.
При использовании ключевого слова ALL условие в операции сравнения должно быть верно для всех значений, которые возвращаются подзапросом. Например, найдем все товары, цена которых меньше чем у любого товара фирмы Apple:
| 1 2 | SELECT * FROM ProductsWHERE Price < ALL(SELECT Price FROM Products WHERE Manufacturer='Apple') |
Если бы мы в данном случае опустили бы ключевое слово ALL, то мы бы столкнулись с ошибкой.
Допустим, если данный подзапрос возвращает значения vаl1, val2 и val3, то условие фильтрации фактически было бы аналогично объединению этих значений через оператор AND:
| 1 | WHERE Price < val1 AND Price < val2 AND Price < val3 |
В тоже время подобный запрос гораздо проще переписать другим образом:
| 12 | SELECT * FROM ProductsWHERE Price < (SELECT MIN(Price) FROM Products WHERE Manufacturer='Apple') |
Как работает оператор ALL:
x > ALL (1, 2)эквивалентноx > 2x < ALL (1, 2)эквивалентноx < 1x = ALL (1, 2)эквивалентно(x = 1) AND (x = 2)x <> ALL (1, 2)эквивалентноx NOT IN (1, 2)
Операторы ANY и SOME условие в операции сравнения должно быть истинным для хотя бы одного из значений, возвращаемых подзапросом. По своему действию оба этих оператора аналогичны, поэтому можно применять любой из них. Например, в следующем случае получим товары, которые стоят меньше самого дорогого товара компании Apple:
| 1 2 | SELECT * FROM ProductsWHERE Price < ANY(SELECT Price FROM Products WHERE Manufacturer='Apple') |
И также стоит отметить, что данный запрос можно сделать проще, переписав следующим образом:
| 1 2 | SELECT * FROM ProductsWHERE Price < (SELECT MAX(Price) FROM Products WHERE Manufacturer='Apple') |
Как работает оператор ANY (а также SOME):
x > ANY (1, 2)эквивалентноx > 1x < ANY (1, 2)эквивалентноx < 2x = ANY (1, 2)эквивалентноx NOT (1, 2)x <> ANY (1, 2)эквивалентно(x <> 1) OR (x <> 2)
Подзапрос как спецификация столбца
Результат подзапроса может представлять отдельный столбец в выборке. Например, выберем все заказы и добавим к ним информацию о названии товара:
| 1 2 3 | SELECT *, (SELECT ProductName FROM Products WHERE Id=Orders.ProductId) AS Product FROM Orders |

Подзапросы в команде INSERT
В команде INSERT подзапросы могут применяться для определения значения, которое вставляется в один из столбцов:
| 1 2 3 4 5 6 7 8 | INSERT INTO Orders (ProductId, CreatedAt, ProductCount, Price)VALUES( (SELECT Id FROM Products WHERE ProductName='Galaxy S8'),'2018-05-23', 2, (SELECT Price FROM Products WHERE ProductName='Galaxy S8')) |
Подзапросы в команде UPDATE
В команде UPDATE подзапросы могут применяться:
- В качестве устанавливаемого значения после оператора SET
- Как часть условия в выражении WHERE
Так, увеличим в таблице Orders количество купленных товаров компании Apple на 2:
| 1 2 3 | UPDATE OrdersSET ProductCount = ProductCount + 2WHERE ProductId IN (SELECT Id FROM Products WHERE Manufacturer='Apple'); |
Или установим для заказа цену товара, полученную в результате подзапроса:
| 1 2 3 | UPDATE OrdersSET Price = (SELECT Price FROM Products WHERE Id=Orders.ProductId) + 3000WHERE Id=1; |
Подзапросы в команде DELETE
В команде DELETE подзапросы также применяются как часть условия. Так, удалим все заказы на Galaxy S8:
| 1 2 | DELETE FROM OrdersWHERE ProductId=(SELECT Id FROM Products WHERE ProductName='Galaxy S8'); |