Подзапросы

Последнее обновление: 29.11.2021

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

Например, определим таблицы для пользователей и компаний, которых работают пользователи:

DROP TABLE IF EXISTS companies;
DROP TABLE IF EXISTS users;
CREATE TABLE companies
(
	id INTEGER PRIMARY KEY AUTOINCREMENT, 
	name TEXT NOT NULL
);
CREATE TABLE users
(
    id INTEGER PRIMARY KEY AUTOINCREMENT,
	name TEXT NOT NULL,
    age INTEGER,
	company_id INTEGER
);

Таблица users содержит поле company_id, которое будет представлять id компании из таблицы companies.

Добавим в таблицы некоторые данные:

INSERT INTO companies (name) VALUES  ('Microsoft'), ('Google');
INSERT INTO users (name, age, company_id) VALUES 
('Tom', 37, (SELECT id FROM companies WHERE name='Microsoft')),
('Bob', 41, (SELECT id FROM companies WHERE name='Google')),
('Sam', 25, (SELECT id FROM companies WHERE name='Microsoft')),
('Alice', 33, (SELECT id FROM companies WHERE name='Google'));

SELECT * FROM users;
подзапросы в SQLite

При добавлении данных в таблицу users как раз используются подзапросы. Например, первый пользователь работает в компании "Microsoft". Соответственно в таблицу users нам надо сохранить id компании, у которой поле name хранит строку "Microsoft". Но на момент написания запроса нам может быть неизвестен ни id компании. В этом случае можно выполнить подзапрос в виде

(SELECT id FROM companies WHERE name='Microsoft')

Подзапрос выполняет команду SELECT и заключается в скобки. В данном же случае при добавлении одного пользователя выполняется один подзапрос, который возвращает одного скалярное значение - числовой идентификатор комании.

В примере выше подзапросы выполнялись к другой таблице, но могут выполняться и к той же, для которой вызывается основной запрос. Например, найдем пользователей из таблицы users, возраст которых выше среднего:

SELECT * FROM users
WHERE age > (SELECT AVG(age) FROM users);

Коррелирующие и некоррелирующие подзапросы

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

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

Например, выберем всех пользователей из таблицы users, добавив к ним информацию о компании:

SELECT name, age, 
		(SELECT name FROM companies 
		WHERE companies.id = users.company_id) AS company
FROM users;

В данном случае для каждой строки из таблицы users будет выполняться подзапрос, результат которого зависит от столбца company_id. И каждый подзапрос может возвращать различные данные.

Correlated subquery in SQLite

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

Коррелирующий подзапрос может выполняться и для той же таблицы, к которой выполняется основной запрос. Например, выберем из таблицы users тех пользователей, возраст которых выше среднего возраста пользователей той же компании:

SELECT name,
	age,
	(SELECT AVG(age) FROM users AS subuser 
	WHERE subuser.company_id=user.company_id) AS avg_age
FROM users AS user
WHERE age > 
	(SELECT AVG(age) FROM users AS subuser 
	WHERE subuser.company_id=user.company_id);
Коррелирующий подзапрос в SQLite

Здесь определено два коррелирующих подзапроса. Первый подзапрос определяет спецификацию столбца avg_age. Он будет выполняться для каждой строки, извлекаемой из таблицы users. В подзапрос передается id компании и на его основе выбирается средняя цена для пользователей именно этой компании. И так как компания у пользователей может отличаться, то и результат подзапроса в каждом случае также может отличаться.

Второй подзапрос аналогичен, только он используется для фильтрации извлекаемых из таблицы users. И также он будет выполняться для каждой строки.

Чтобы избежать двойственности при фильтрации в подзапросе при сравнении id компаний (subuser.company_id=user.company_id) для внешней выборки установлен псевдоним user, а для выборки из подзапросов определен псевдоним subuser.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850