T-SQL поддерживает функциональность подзапросов (subquery), то есть таких запросов, которые могут встроены в другие запросы.
Например, создадим таблицы для товаров, покупателей и заказов:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| USE productsdb; CREATE TABLE Products ( Id INT IDENTITY PRIMARY KEY , ProductName NVARCHAR(30) NOT NULL , Manufacturer NVARCHAR(20) NOT NULL , ProductCount INT DEFAULT 0, Price MONEY NOT NULL ); CREATE TABLE Customers ( Id INT IDENTITY PRIMARY KEY , FirstName NVARCHAR(30) NOT NULL ); CREATE TABLE Orders ( Id INT IDENTITY PRIMARY KEY , ProductId INT NOT NULL REFERENCES Products(Id) ON DELETE CASCADE , CustomerId INT NOT NULL REFERENCES Customers(Id) ON DELETE CASCADE , CreatedAt DATE NOT NULL , ProductCount INT DEFAULT 1, Price MONEY NOT NULL ); |
Таблица Orders содержит ссылки на две другие таблицы через поля ProductId и CustomerId.
Добавим в таблицы некоторые данные:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| INSERT INTO Products VALUES ( 'iPhone 6' , 'Apple' , 2, 36000), ( 'iPhone 6S' , 'Apple' , 2, 41000), ( 'iPhone 7' , 'Apple' , 5, 52000), ( 'Galaxy S8' , 'Samsung' , 2, 46000), ( 'Galaxy S8 Plus' , 'Samsung' , 1, 56000), ( 'Mi 5X' , 'Xiaomi' , 2, 26000), ( 'OnePlus 5' , 'OnePlus' , 6, 38000) INSERT INTO Customers VALUES ( 'Tom' ), ( 'Bob' ),( 'Sam' ) INSERT INTO Orders VALUES ( ( SELECT Id FROM Products WHERE ProductName= 'Galaxy S8' ), ( SELECT Id FROM Customers WHERE FirstName= 'Tom' ), '2017-07-11' , 2, ( SELECT Price FROM Products WHERE ProductName= 'Galaxy S8' ) ), ( ( SELECT Id FROM Products WHERE ProductName= 'iPhone 6S' ), ( SELECT Id FROM Customers WHERE FirstName= 'Tom' ), '2017-07-13' , 1, ( SELECT Price FROM Products WHERE ProductName= 'iPhone 6S' ) ), ( ( SELECT Id FROM Products WHERE ProductName= 'iPhone 6S' ), ( SELECT Id FROM Customers WHERE FirstName= 'Bob' ), '2017-07-11' , 1, ( SELECT Price FROM Products WHERE ProductName= 'iPhone 6S' ) ) |
Здесь интерес представляет добавление элементов в таблицу Orders. Например, первый заказ был сделан покупателем Tom на товар Galaxy S8. Соответственно в таблицу Orders нам надо сохранить информацию о заказе, где поле ProductId указывает на Id товара Galaxy S8, поле Price - на его цену, а поле CustomerId - на Id покупателя Tom. Но на момент написания запроса нам может быть неизвестен ни Id покупателя, ни Id товара, ни цена товара. В этом случае можно выполнить подзапрос.
Подзапрос выполняет команду SELECT и заключается в скобки. В данном же случае при добавлении одного товара выполняется три подзапроса. Каждый подзапрос возвращает одного скалярное значение, например, числовой идентификатор.
В данном случае подзапросы выполнялись к другой таблице, но могут выполняться и к той же, к которой вызывается основной запрос. Например, найдем товары из таблицы Products, которые имеют минимальную цену:
1
2
3
| SELECT * FROM Products WHERE Price = ( SELECT MIN (Price) FROM Products) |
Или найдем товары, цена которых выше средней:
1
2
3
| SELECT * FROM Products WHERE Price > ( SELECT AVG (Price) FROM Products) |
Коррелирующие подзапросы
Подзапросы бывают коррелирующими и некоррелирующими. В примерах выше команды SELECT выполняли фактически один подзапрос для всей команды, например, подзапрос возвращает минимальную или среднюю цену, которая не изменится, сколько бы мы строк не выбирали в основном запросе. То есть результат подзапроса не зависел от строк, которые выбираются в основном запросе. И такой подзапрос выполняется один раз для всего внешнего запроса.
Но также существуют коррелирующие подзапросы (correlated subquery), результаты которых зависят от строк, которые выбираются в основном запросе.
Например, выберем все заказы из таблицы Orders, добавив к ним информацию о товаре:
1
2
3
4
5
| SELECT CreatedAt, Price, ( SELECT ProductName FROM Products WHERE Products.Id = Orders.ProductId) AS Product FROM Orders |
Здесь для каждой строки из таблицы Orders будет выполняться подзапрос, результат которого зависит от столбца ProductId. И каждый подзапрос может возвращать различные данные.
Коррелирующий подзапрос может выполняться и для той же таблицы, к которой выполняется основной запрос. Например, выберем из таблицы Products те товары, стоимость которых выше средней цены товаров для данного производителя:
1
2
3
4
5
6
7
8
9
| SELECT ProductName, Manufacturer, Price, ( SELECT AVG (Price) FROM Products AS SubProds WHERE SubProds.Manufacturer=Prods.Manufacturer) AS AvgPrice FROM Products AS Prods WHERE Price > ( SELECT AVG (Price) FROM Products AS SubProds WHERE SubProds.Manufacturer=Prods.Manufacturer) |
В данном случае определено два коррелирующих подзапроса. Первый подзапрос определяет спецификацию столбца AvgPrice. Он будет выполняться для каждой строки, извлекаемой из таблицы Products. В подзапрос передается производитель товара и на его основе выбирается средняя цена для товаров именно этого производителя. И так как производитель у товаров может отличаться, то и результат подзапроса в каждом случае также может отличаться.
Второй подзапрос аналогичен, только он используется для фильтрации извлекаемых из таблицы Products. И также он будет выполняться для каждой строки.
Чтобы избежать двойственности при фильтрации в подзапросе при сравнении производителей (
SubProds.Manufacturer=Prods.Manufacturer
) для внешней выборки установлен псевдоним Prods, а для выборки из подзапросов определен псевдоним SubProds.
Следует учитывать, что коррелирующие подзапросы выполняются для каждой отдельной строки выборки, то выполнение таких подзапросов может замедлять выполнение всего запроса в целом.
Комментариев нет:
Отправить комментарий