Введение
В этой лабораторной работе вы изучите возможности подзапросов (subqueries) и вложенных операций в MySQL. Основное внимание уделяется использованию подзапросов в предложении WHERE для фильтрации данных на основе условий, полученных из других таблиц или той же таблицы.
Вы научитесь подключаться к серверу MySQL, создавать базу данных и таблицы (customers и orders), а затем конструировать SQL-запросы, использующие подзапросы, для выявления клиентов, разместивших заказы на общую сумму, превышающую определенное значение. Лабораторная работа также охватывает использование EXISTS с подзапросом, тестирование коррелированных подзапросов и сравнение производительности подзапросов.
Написание подзапроса в предложении WHERE
В этом шаге вы научитесь использовать подзапрос (subquery) в предложении WHERE SQL-запроса. Подзапрос — это запрос, вложенный в другой запрос. Это мощный инструмент для извлечения данных на основе условий, полученных из других таблиц или той же таблицы.
Понимание подзапросов
Подзапрос (или внутренний запрос) — это SQL-запрос, вложенный в более крупный запрос. Подзапрос выполняется первым, а его результат используется внешним запросом. Подзапросы могут встречаться в предложениях WHERE, SELECT, FROM и HAVING.
В предложении WHERE подзапрос обычно используется для фильтрации результатов внешнего запроса на основе условия. Подзапрос возвращает одно значение или набор значений, которые внешний запрос использует для сравнения.
Сценарий
Представьте, что у вас есть две таблицы: customers и orders. Таблица customers содержит информацию о клиентах (например, customer_id, name, city), а таблица orders содержит информацию о заказах (например, order_id, customer_id, order_date, total_amount).
Вы хотите найти всех клиентов, которые разместили хотя бы один заказ на сумму более 100 долларов.
Шаги
Подключение к серверу MySQL:
Откройте терминал и выполните следующую команду для подключения к серверу MySQL от имени пользователя
root:sudo mysql -u rootВы должны увидеть приглашение MySQL:
mysql>.Создание базы данных и таблиц:
Если у вас еще нет базы данных и таблиц, создайте их сейчас. Давайте создадим базу данных с именем
labdbи таблицыcustomersиorders. Выполните следующие SQL-команды в приглашении MySQL:CREATE DATABASE IF NOT EXISTS labdb; USE labdb; CREATE TABLE IF NOT EXISTS customers ( customer_id INT PRIMARY KEY, name VARCHAR(255), city VARCHAR(255) ); CREATE TABLE IF NOT EXISTS orders ( order_id INT PRIMARY KEY AUTO_INCREMENT, customer_id INT, order_date DATE, total_amount DECIMAL(10, 2), FOREIGN KEY (customer_id) REFERENCES customers(customer_id) );Вставка образца данных:
Вставьте некоторые образцы данных в таблицы. Выполните следующие SQL-команды в приглашении MySQL:
INSERT INTO customers (customer_id, name, city) VALUES (1, 'Alice Smith', 'New York'), (2, 'Bob Johnson', 'Los Angeles'), (3, 'Charlie Brown', 'Chicago'), (4, 'David Lee', 'Houston'); INSERT INTO orders (customer_id, order_date, total_amount) VALUES (1, '2023-01-15', 120.00), (2, '2023-02-20', 80.00), (1, '2023-03-10', 150.00), (3, '2023-04-05', 200.00), (2, '2023-05-12', 110.00), (4, '2023-06-18', 90.00);Написание подзапроса в предложении WHERE:
Теперь напишите запрос для поиска клиентов, которые разместили заказы на сумму более 100 долларов. Выполните следующую SQL-команду в приглашении MySQL:
SELECT * FROM customers WHERE customer_id IN (SELECT customer_id FROM orders WHERE total_amount > 100);Объяснение:
- Подзапрос
(SELECT customer_id FROM orders WHERE total_amount > 100)выбираетcustomer_idиз таблицыorders, гдеtotal_amountбольше 100. - Внешний запрос
SELECT * FROM customers WHERE customer_id IN (...)выбирает все столбцы из таблицыcustomers, гдеcustomer_idнаходится в набореcustomer_id, возвращенных подзапросом.
- Подзапрос
Наблюдение за выводом:
Вы должны увидеть следующий вывод, показывающий клиентов, которые разместили заказы на сумму более 100 долларов:
+-------------+-------------+-----------+ | customer_id | name | city | +-------------+-------------+-----------+ | 1 | Alice Smith | New York | | 2 | Bob Johnson | Los Angeles | | 3 | Charlie Brown | Chicago | +-------------+-------------+-----------+ 3 rows in set (0.00 sec)
Использование EXISTS с подзапросом
В этом шаге вы научитесь использовать оператор EXISTS с подзапросом в MySQL. Оператор EXISTS используется для проверки существования строк в подзапросе. Он возвращает TRUE, если подзапрос возвращает какие-либо строки, и FALSE в противном случае.
Понимание EXISTS
Оператор EXISTS часто используется в предложении WHERE SQL-запроса для фильтрации результатов на основе существования связанных данных в другой таблице. Это мощная альтернатива использованию операций IN или JOIN, и иногда может быть более эффективной, особенно при работе с большими наборами данных.
В отличие от IN, EXISTS фактически не извлекает данные из подзапроса. Он просто проверяет, возвращаются ли какие-либо строки. Это может сделать его быстрее, чем IN, когда вам нужно только знать, существует ли совпадение, а не сами значения.
Сценарий
Продолжая использовать таблицы customers и orders из предыдущего шага, найдем всех клиентов, которые разместили хотя бы один заказ.
Предварительные требования
Убедитесь, что вы выполнили предыдущий шаг ("Написание подзапроса в предложении WHERE") и что база данных labdb, таблица customers и таблица orders заполнены данными.
Шаги
Написание запроса с использованием EXISTS:
Напишите запрос для поиска клиентов, которые разместили хотя бы один заказ. Выполните следующую SQL-команду в приглашении MySQL:
SELECT * FROM customers c WHERE EXISTS ( SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id );Объяснение:
- Внешний запрос
SELECT * FROM customers cвыбирает все столбцы из таблицыcustomersс псевдонимомc. - Предложение
WHERE EXISTS (...)проверяет, возвращает ли подзапрос какие-либо строки. - Подзапрос
SELECT 1 FROM orders o WHERE o.customer_id = c.customer_idвыбирает значение1(это может быть любое константное значение) из таблицыordersс псевдонимомo, гдеcustomer_idв таблицеordersсовпадает сcustomer_idв таблицеcustomers. - Оператор
EXISTSвозвращаетTRUE, если подзапрос возвращает хотя бы одну строку, что указывает на то, что клиент разместил хотя бы один заказ.
- Внешний запрос
Наблюдение за выводом:
Вы должны увидеть следующий вывод, показывающий всех клиентов, которые разместили хотя бы один заказ:
+-------------+-------------+-----------+ | customer_id | name | city | +-------------+-------------+-----------+ | 1 | Alice Smith | New York | | 2 | Bob Johnson | Los Angeles | | 3 | Charlie Brown | Chicago | | 4 | David Lee | Houston | +-------------+-------------+-----------+ 4 rows in set (0.00 sec)Изменение запроса (необязательно):
Изменим запрос, чтобы найти клиентов, которые не размещали никаких заказов. Вы можете сделать это, используя
NOT EXISTS. Выполните следующую SQL-команду в приглашении MySQL:SELECT * FROM customers c WHERE NOT EXISTS ( SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id );Наблюдение за выводом:
Поскольку все клиенты в наших образцовых данных разместили заказы, этот запрос должен вернуть пустой набор результатов:
Empty set (0.00 sec)
Тестирование коррелированного подзапроса
В этом шаге вы узнаете о коррелированных подзапросах (correlated subqueries) в MySQL. Коррелированный подзапрос — это подзапрос, который ссылается на столбец из внешнего запроса. Это означает, что подзапрос выполняется один раз для каждой строки во внешнем запросе.
Понимание коррелированных подзапросов
В отличие от простого подзапроса, который выполняется только один раз, коррелированный подзапрос зависит от внешнего запроса для получения своих значений. Подзапрос использует значения из текущей строки внешнего запроса для определения своего результата. Это делает коррелированные подзапросы более мощными для определенных типов запросов, но также потенциально менее эффективными, чем простые подзапросы, особенно для больших наборов данных.
Сценарий
Продолжая использовать таблицы customers и orders, найдем всех клиентов, которые разместили заказ на сумму, превышающую среднюю сумму заказа по всем заказам.
Предварительные требования
Убедитесь, что вы выполнили предыдущие шаги и что база данных labdb, таблица customers и таблица orders заполнены данными.
Шаги
Написание коррелированного подзапроса:
Напишите запрос для поиска клиентов, которые разместили заказ на сумму, превышающую среднюю сумму заказа. Выполните следующую SQL-команду в приглашении MySQL:
SELECT c.customer_id, c.name FROM customers c WHERE EXISTS ( SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id AND o.total_amount > (SELECT AVG(total_amount) FROM orders) );Объяснение:
- Внешний запрос
SELECT c.customer_id, c.name FROM customers cвыбираетcustomer_idиnameиз таблицыcustomersс псевдонимомc. - Предложение
WHERE EXISTS (...)проверяет, возвращает ли подзапрос какие-либо строки. - Коррелированный подзапрос
SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id AND o.total_amount > (SELECT AVG(total_amount) FROM orders)выбирает значение1из таблицыordersс псевдонимомo, где:o.customer_id = c.customer_id: Это корреляция. Подзапрос ссылается наcustomer_idиз таблицыcustomersвнешнего запроса.o.total_amount > (SELECT AVG(total_amount) FROM orders): Это условие проверяет, превышает ли сумма заказа среднюю сумму заказа по всем заказам. ПодзапросAVG(total_amount)является некоррелированным подзапросом, который выполняется только один раз для получения средней суммы заказа.
- Внешний запрос
Наблюдение за выводом:
Вы должны увидеть следующий вывод, показывающий клиентов, которые разместили заказы на сумму, превышающую среднюю сумму заказа:
+-------------+-------------+ | customer_id | name | +-------------+-------------+ | 1 | Alice Smith | | 3 | Charlie Brown | +-------------+-------------+ 2 rows in set (0.00 sec)Другой пример: Найти самую высокую сумму заказа для каждого клиента
Выполните следующую SQL-команду в приглашении MySQL, чтобы получить идентификатор, имя каждого клиента и их самую высокую сумму заказа. Коррелированный подзапрос вычисляет максимальную сумму заказа для каждого клиента индивидуально.
SELECT c.customer_id, c.name, ( SELECT MAX(o.total_amount) FROM orders o WHERE o.customer_id = c.customer_id ) AS highest_order_amount FROM customers c;Наблюдение за выводом:
Вы должны увидеть следующий вывод:
+-------------+-------------+-----------------------+ | customer_id | name | highest_order_amount | +-------------+-------------+-----------------------+ | 1 | Alice Smith | 150.00 | | 2 | Bob Johnson | 110.00 | | 3 | Charlie Brown | 200.00 | | 4 | David Lee | 90.00 | +-------------+-------------+-----------------------+ 4 rows in set (0.00 sec)
Сравнение производительности подзапросов
В этом шаге вы научитесь сравнивать производительность различных типов подзапросов в MySQL. Понимание характеристик производительности подзапросов имеет решающее значение для написания эффективных SQL-запросов, особенно при работе с большими наборами данных.
Понимание факторов производительности
На производительность подзапроса могут влиять несколько факторов, в том числе:
- Размер данных: Размер таблиц, участвующих в запросе.
- Тип подзапроса: Является ли подзапрос коррелированным или некоррелированным.
- Индексирование: Наличие и эффективность индексов в таблицах.
- Версия MySQL: Конкретная используемая версия MySQL, поскольку методы оптимизации запросов могут различаться.
Сценарий
Продолжая использовать таблицы customers и orders, сравним производительность подзапроса с использованием IN и подзапроса с использованием EXISTS для поиска всех клиентов, которые разместили хотя бы один заказ.
Предварительные требования
Убедитесь, что вы выполнили предыдущие шаги и что база данных labdb, таблица customers и таблица orders заполнены данными. Чтобы сделать сравнение производительности более осмысленным, мы добавим больше данных в таблицу orders.
Шаги
Добавление большего количества данных в таблицу
orders:Чтобы сделать сравнение производительности более реалистичным, добавим значительный объем данных в таблицу
orders. Мы вставим 1000 заказов для каждого клиента с помощью хранимой процедуры. Выполните следующие SQL-команды в приглашении MySQL:DELIMITER // CREATE PROCEDURE insert_many_orders() BEGIN DECLARE i INT DEFAULT 1; WHILE i <= 1000 DO INSERT INTO orders (customer_id, order_date, total_amount) VALUES (1, CURDATE(), 50.00); INSERT INTO orders (customer_id, order_date, total_amount) VALUES (2, CURDATE(), 75.00); INSERT INTO orders (customer_id, order_date, total_amount) VALUES (3, CURDATE(), 100.00); INSERT INTO orders (customer_id, order_date, total_amount) VALUES (4, CURDATE(), 125.00); SET i = i + 1; END WHILE; END// DELIMITER ; CALL insert_many_orders(); DROP PROCEDURE insert_many_orders;Объяснение:
- Этот SQL-скрипт создает хранимую процедуру с именем
insert_many_orders. - Процедура вставляет 1000 заказов для каждого из четырех клиентов в таблицу
orders. - После вставки данных процедура удаляется.
- Этот SQL-скрипт создает хранимую процедуру с именем
Запрос с использованием
IN:Выполните следующий запрос с использованием
INдля поиска всех клиентов, которые разместили хотя бы один заказ:SELECT * FROM customers WHERE customer_id IN (SELECT customer_id FROM orders);Анализ плана выполнения запроса с помощью
EXPLAIN:Перед выполнением запроса используйте команду
EXPLAINдля анализа плана выполнения запроса. Это даст вам представление о том, как MySQL планирует выполнить запрос, и поможет выявить потенциальные узкие места в производительности. Выполните следующую SQL-команду в приглашении MySQL:EXPLAIN SELECT * FROM customers WHERE customer_id IN (SELECT customer_id FROM orders);Вывод
EXPLAINпокажет вам доступные таблицы, используемые индексы (если таковые имеются) и порядок выполнения операций. Обратите внимание на столбецtype, который указывает тип используемого соединения или метода доступа.Запрос с использованием
EXISTS:Выполните следующий запрос с использованием
EXISTSдля поиска всех клиентов, которые разместили хотя бы один заказ:SELECT * FROM customers c WHERE EXISTS ( SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id );Анализ плана выполнения запроса с помощью
EXPLAIN:Используйте команду
EXPLAINдля анализа плана выполнения запросаEXISTS. Выполните следующую SQL-команду в приглашении MySQL:EXPLAIN SELECT * FROM customers c WHERE EXISTS ( SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id );Сравните план выполнения с планом запроса
IN. Есть ли какие-либо различия в доступных таблицах, используемых индексах или методах доступа?Наблюдения:
В целом,
EXISTSобычно работает лучше, чемIN, когда подзапрос возвращает большое количество строк. Это связано с тем, чтоINдолжен сравнивать значения внешнего запроса со всеми значениями, возвращаемыми подзапросом, в то время какEXISTSостанавливается, как только находит совпадение. Однако фактическая производительность может варьироваться в зависимости от конкретного запроса, данных и системы управления базами данных. Вы можете использовать функциюBENCHMARK()(как показано в исходном документе), чтобы получить более точное измерение времени выполнения, но для этой лабораторной работы анализ выводаEXPLAINдает достаточное представление о планах запросов.Очистка (необязательно):
Если вы хотите очистить базу данных и таблицы, вы можете выполнить следующие команды в приглашении MySQL:
DROP TABLE IF EXISTS orders; DROP TABLE IF EXISTS customers; DROP DATABASE IF EXISTS labdb;Когда вы закончите все шаги, вы можете выйти из клиента MySQL, набрав:
exit
Резюме
В этой лабораторной работе вы научились использовать подзапросы в предложении WHERE SQL-запроса для фильтрации данных на основе условий, полученных из других таблиц или той же таблицы. Вы практиковались в подключении к серверу MySQL, создании базы данных и таблиц, а также вставке образцов данных.
Вы изучили использование оператора IN с подзапросом для поиска клиентов на основе связанных данных в таблице orders. Вы также узнали об операторе EXISTS как альтернативе IN и практиковались в его использовании для проверки существования связанных строк.
Кроме того, вы познакомились с коррелированными подзапросами, которые ссылаются на столбцы из внешнего запроса, и использовали один из них для поиска клиентов с заказами, превышающими среднюю сумму заказа. Наконец, вы сравнили производительность подзапросов IN и EXISTS, анализируя их планы выполнения с помощью команды EXPLAIN, получив представление о том, как MySQL обрабатывает эти запросы.



