MySQL підзапити


Підзапити представляють вирази SELECT , які вбудовані в інші запити SQL. Розглянемо найпростіший приклад застосування підзапитів.

Наприклад, створимо таблиці для товарів і замовлень:

Таблиця Orders містить дані про купленим товарам з таблиці Products .

CREATE TABLE Products
(
    Id INT AUTO_INCREMENT PRIMARY KEY,
    ProductName VARCHAR(30) NOT NULL,
    Manufacturer VARCHAR(20) NOT NULL,
    ProductCount INT DEFAULT 0,
    Price DECIMAL NOT NULL
);
CREATE TABLE Orders
(
    Id INT AUTO_INCREMENT PRIMARY KEY,
    ProductId INT NOT NULL,
    ProductCount INT DEFAULT 1,
    CreatedAt DATE NOT NULL,
    Price DECIMAL NOT NULL,
    FOREIGN KEY (ProductId) REFERENCES Products(Id) ON DELETE CASCADE
);

Додамо в таблицю деякі дані:

INSERT INTO Products (ProductName, Manufacturer, ProductCount, Price)
VALUES ('iPhone X', 'Apple', 2, 76000),
('iPhone 8', 'Apple', 2, 51000),
('iPhone 7', 'Apple', 5, 42000),
('Galaxy S9', 'Samsung', 2, 56000),
('Galaxy S8', 'Samsung', 1, 46000),
('Honor 10', 'Huawei', 2, 26000),
('Nokia 8', 'HMD Global', 6, 38000);
 
INSERT INTO Orders (ProductId, CreatedAt, ProductCount, Price)
VALUES
( 
    (SELECT Id FROM Products WHERE ProductName='Galaxy S8'),
    '2018-05-21', 
    2, 
    (SELECT Price FROM Products WHERE ProductName='Galaxy S8')
),
( 
    (SELECT Id FROM Products WHERE ProductName='iPhone X'),
    '2018-05-23',  
    1, 
    (SELECT Price FROM Products WHERE ProductName='iPhone X')
),
( 
    (SELECT Id FROM Products WHERE ProductName='iPhone 8'),
    '2018-05-21',  
    1, 
    (SELECT Price FROM Products WHERE ProductName='iPhone 8')
);

При додаванні даних в таблицю Orders якраз використовує підзапити. Наприклад, перше замовлення було зроблено на товар Galaxy S8. Відповідно до таблиці Orders нам треба зберегти інформацію про замовлення, де поле ProductId вказує на Id товару Galaxy S 8, поле Price - на його ціну. Але на момент написання запиту нам може бути невідомий ні Id покупця, ні Id товару, ні ціна товару. В цьому випадку можна виконати підзапит в вигляді

Підзапит виконує команду SELECT і полягає в дужки. В даному ж випадку при додаванні одного товару виконується два підзапиту. Кожен підзапит повертає одного скалярний значення, наприклад, числовий ідентифікатор.

В наведеному вище прикладі підзапити виконувалися до іншої таблиці, але можуть виконуватися і до тієї ж, для якої викликається основний запит. Наприклад, знайдемо товари з таблиці Products, які мають мінімальну ціну:

Або знайдемо товари, ціна яких перевищує середню:

SELECT * FROM Products
WHERE Price > (SELECT AVG(Price) FROM Products);

Корелюючі і некорелюючі підзапити

Підзапити бувають коррелирующими і некоррелірующімі. У прикладах вище команди SELECT фактично виконували один підзапит для всіх рядків, що витягають командою. Наприклад, підзапит повертає мінімальну або середню ціну, яка не зміниться, скільки б ми рядків не вибирали в основному запиті. Тобто результат підзапиту не залежав від рядків, які вибираються в основному запиті. І такий підзапит виконується один раз для всього зовнішнього запиту.

Але також можна використовувати і корелюючі підзапити (correlated subquery), результати яких залежать від рядків, які вибираються в основному запиті.

Наприклад, виберемо всі замовлення з таблиці Orders , додавши до них інформацію про товар:

SELECT  CreatedAt, Price,
        (SELECT ProductName FROM Products
        WHERE Products.Id = Orders.ProductId) AS Product
FROM Orders;

 

В даному випадку для кожного рядка з таблиці Orders буде виконуватися підзапит, результат якого залежить від стовпчика ProductId . І кожен підзапит може повертати різні дані.

Корелюючий підзапит може виконуватися і для тієї ж таблиці, до якої виконується основний запит. Наприклад, виберемо з таблиці Products ті товари, вартість яких вища за середню ціну товарів для даного виробника:

Тут визначено два корелюючі підзапита. Перший підзапит визначає специфікацію стовпця AvgPrice . Він буде виконуватися для кожного рядка, що витягується з таблиці Products . У підзапит передається виробник товару і на його основі вибирається середня ціна для товарів саме цього виробника. І так як виробник у товарів може відрізнятися, то і результат підзапиту в кожному випадку також може відрізнятися.

Другий підзапит аналогічний, тільки він використовується для фільтрації витягають із таблиці Products . І також він буде виконуватися для кожного рядка.

Щоб уникнути подвійності при фільтрації в підзапиті при порівнянні виробників ( SubProds.Manufacturer=Prods.Manufacturer ) для зовнішньої вибірки встановлений псевдонім Prods , а для вибірки з підзапитів визначено псевдонім SubProds .

Слід враховувати, що корелюючі підзапити виконуються для кожної окремого рядка вибірки, то виконання таких підзапитів може уповільнювати виконання всього запиту в цілому.


Наш партнер:
beta test mp3 playlist downloader