Семинар 4. Сложные запросы, подзапросы. Целостность.
1. Сложные запросы и подзапросы
1.1. Структура запроса
Порядок написания запроса
SELECT
[ALL | DISTINCT [ON (expression [, ...] )] ]
[* | expression [AS output_name] [, ...] ]
[FROM from_item [, ...] ]
[WHERE condition]
[GROUP BY grouping_element [, ...]]
[HAVING condition]
[ORDER BY expression [ASC | DESC | USING operator] [NULLS {FIRST | LAST}]
[, ...]]
[LIMIT {count | ALL}]
[OFFSET start [ROW | ROWS]]
[FETCH {FIRST | NEXT} [count] {ROW | ROWS} ONLY]
1.2 Ключевое слово WITH
WITH предоставляет способ записывать дополнительные операторы для применения в больших запросах.
Эти операторы, которые также называют общими табличными выражениями (Common Table Expressions, CTE),
можно представить как определения временных таблиц, существующих только для одного запроса.
Более подробно про СТЕ будет на следующих семинарах.
Пример:
WITH
regional_sales AS (
SELECT
region,
SUM(amount) AS total_sales
FROM
orders
GROUP BY
region
),
top_regions AS (
SELECT
region
FROM
regional_sales
WHERE
total_sales > (SELECT SUM(total_sales)/10 FROM regional_sales)
)
SELECT
region,
product,
SUM(quantity) AS product_units,
SUM(amount) AS product_sales
FROM
orders
WHERE
region IN (SELECT region FROM top_regions)
GROUP BY
region,
product;
1.3 Подзапросы
Подзапрос — запрос, содержащийся в другом SQL-запросе. Запрос, содержащий другой подзапрос, называется содержащим выражением.
- Подзапрос всегда заключен в круглые скобки и обычно выполняется до содержащего выражения.
- Подзапросы могут вкладываться друг в друга.
- В
SELECT’eподзапросы можно использовать во всех разделах, кромеGROUP BY.
Классификация подзапросов:
- По взаимодействию с содержащим выражением:
- Связанные (т.е. ссылающиеся на столбцы основного запроса):
- Для написания таких запросов полезно использование алиасов. (
SELECT … AS T) - Для случаев, когда в основном запросе и в подзапросе используется одна и та же таблица, использование алиасов обязательно!
- Выполняются для каждой строки содержащего выражения.
- Для написания таких запросов полезно использование алиасов. (
- Несвязанные (т.е. полностью самодостаточные и не зависящие от основного запроса) — выполняются перед выполнением содержащего выражения.
- Связанные (т.е. ссылающиеся на столбцы основного запроса):
- По результату выполнения:
- Скалярные (1 столбец и 1 строка)
- Нескалярные
Использование подзапросов:

Примеры использования подзапросов в DELETE, UPDATE и INSERT:
DELETE FROM Classes
WHERE NOT EXISTS (SELECT * FROM Marks WHERE Marks.class_id = Classes.id);
UPDATE Classes
SET featured = 1
WHERE (SELECT count(*) FROM marks WHERE class_id = id) > 10;
INSERT INTO BestStudents2024(`name`, `surname`)
SELECT name, surname FROM Students
WHERE (SELECT AVG(mark) FROM Marks WHERE Students.id = Marks.student_id) > (SELECT AVG(mark) FROM Marks);
1.4 Предикаты (для подзапросов вида 1 столбец и несколько строк)
EXISTS— Значением условияEXISTSявляетсяTRUEв том и только в том случае, когда мощность таблицы-результата подзапроса больше нуля, иначе значением условия являетсяFALSE:
SELECT SupplierName
FROM Suppliers
WHERE EXISTS(
SELECT ProductName
FROM Products
WHERE SupplierId = Suppliers.supplierId
AND Price < 20);
IN— ПредикатINдля подзапросов работает так же, как и для обычных запросов (проверка наличия значения в списке):
SELECT emp_id
, fname
, lname
, title
FROM employee
WHERE emp_id IN(
SELECT superior_emp_id
FROM employee);
ALL—TRUE, если результат подзапроса пуст или значение предиката равно TRUE для каждой строки подзапроса; если хотя бы что-тоFALSE, то вернетFALSE, во всех остальных случаях вернетUNKNOWN:
SELECT EMP_NO
FROM EMP
WHERE DEPT_NO = 65
AND EMP_SAL >= ALL(
SELECT EMP1.EMP_SAL
FROM EMP AS EMP1
WHERE EMP.DEPT_NO = EMP1.DEPT_NO);
ANY—FALSE, если результат подзапроса пуст или значение условия равноFALSEдля каждой строки подзапроса; если хотя бы что-тоTRUE, то вернетTRUE, в остальных случаях вернетUNKNOWN:
SELECT EMP_NO
FROM EMP
WHERE DEPT_NO = 65
AND EMP_SAL > ANY(
SELECT EMP1.EMP_SAL
FROM EMP AS EMP1
WHERE EMP.DEPT_NO = EMP1.DEPT_NO);
CREATE TABLE AS— создаёт таблицу и наполняет её данными, полученными в результате выполненияSELECT. Столбцы этой таблицы получают имена и типы данных в соответствии со столбцами результатаSELECT(хотя имена столбцов можно переопределить, добавив явно список новых имен столбцов).
CREATE TABLE NEW_TABLE AS
SELECT *
FROM OLD_TABLE;
CREATE TABLE AS напоминает создание представления, но на самом деле есть значительная разница: эта команда создает новую таблицу и выполняет запрос только раз, чтобы наполнить таблицу начальными данными. Последующие изменения в исходных таблицах запроса в новой таблице отражаться не будут. С представлением, напротив, определяющая его команда SELECT выполняется при каждой выборке из него.
3. Практическое задание (сложные запросы + подзапросы)
- С помощью скалярного подзапроса найти имена преподавателей, которые получили разово минимальную выплату за все время.
- С помощью скалярного подзапроса найти имена преподавателей, у которых выплата по тому или иному типу была максимальной. Вывести имя преподавателя и тип выплаты, используя case или decode (1 – выплата за семинарские занятия, 2 – выплата за лекционные занятия, 3 – премиальная часть);
- С помощью подзапроса и предиката
INвывести имена преподавателей, тип и сумму выплат по каждому типу за все время работы, при условии, что у преподавателя была выплата за лекционные занятия. - С помощью
EXISTSвывести имена преподавателей, тип и сумму выплат по каждому типу за все время работы, при условии, что у преподавателя была выплата за лекционные занятия; - С помощью аналитической функции
COUNTнайти количество выплат по каждому преподавателю за каждое число; выведите также размер каждой выплаты. - С помощью аналитической функции
SUMнайти общий размер выплат по каждому преподавателю за каждое число; выведите также размер каждой выплаты. - С помощью аналитической функции
SUMи сортировки найти суммарные выплаты по каждому преподавателю за каждое число (по нарастанию).