Бывает так, что подзапрос одиночной строки возвращает более одной строки. В таком случае возникнет ошибка.
Для каждого магазина найдем одного сотрудника с должностью 'MANAGER'
.
SELECT s.store_id,
s.name,
(SELECT e.employee_id
FROM employee e
WHERE e.store_id = s.store_id
AND e.rank_id = 'MANAGER'
) AS employee_id
FROM store s
ORDER BY s.store_id
error: more than one row returned by a subquery used as an expression
Посмотрим, что там с данными не так?
SELECT e.store_id,
e.rank_id,
e.last_name || ' ' || e.first_name AS full_name
FROM employee e
WHERE e.rank_id = 'MANAGER'
ORDER BY e.store_id, e.last_name, e.first_name
# | store_id | rank_id | full_name |
---|---|---|---|
1 | 100 | MANAGER | Тарасов Глеб |
2 | 200 | MANAGER | Калашников Владислав |
3 | 201 | MANAGER | Зайцева Никки |
4 | 201 | MANAGER | Тимофеева Доминика |
5 | 300 | MANAGER | Бирюков Владислав |
6 | 301 | MANAGER | Емельянова Кристина |
7 | 302 | MANAGER | Кондратьева Марина |
8 | 400 | MANAGER | Медведев Валерий |
9 | 500 | MANAGER | Картман Эрик |
10 | 600 | MANAGER | Воробьёв Николай |
11 | 600 | MANAGER | Калинин Мирон |
12 | 600 | MANAGER | Кулаков Вилен |
13 | 800 | MANAGER | Белозёров Зиновий |
14 | 900 | MANAGER | Авдеев Павел |
В магазине 201 два менеджера, а в магазине 600 - три.
В такой ситуации есть два варианта развития событий. Мы можем решать неправильную задачу. Если в магазине может быть несколько менеджеров, то мы должны работать с массивом менеджеров. В таком случае может быть приемлемым получить одного из них, например, первого по алфавиту.
Возможно, менеджер в магазине должен быть только один. Просто кто-то не создал уникальный ключ, и пользователи создали несколько менеджеров в магазине. В таком случае, мы написали правильный запрос и необходимо избавляться от неправильных данных и дорабатывать структуру данных.
В любом случае, необходимо гарантировать, что в результате подзапроса будет возвращено не более одной строки. Для этого необходимо одно из:
- должен быть уникальный ключ, гарантирующий, что в результате подзапроса будет не более одной строки;
- использовать агрегатную функцию;
- использовать
LIMIT 1
для ограничения количества строк.
Воспользуемся LIMIT 1
:
SELECT s.store_id,
s.name,
(SELECT e.employee_id
FROM employee e
WHERE e.store_id = s.store_id
AND e.rank_id = 'MANAGER'
ORDER BY e.last_name,
e.first_name,
e.middle_name
LIMIT 1
) AS employee_id
FROM store s
ORDER BY s.store_id
# | store_id | name | employee_id |
---|---|---|---|
1 | 100 | Пионер | 11 |
2 | 200 | Марс | 12 |
3 | 201 | Сатурн | 21 |
4 | 300 | Адалин | 13 |
5 | 301 | Адалин-family | 22 |
6 | 302 | Адалин-ultra | 24 |
7 | 400 | Европа | 14 |
8 | 500 | Март | 15 |
9 | 600 | Umi | 16 |
10 | 800 | Универсам | 19 |
11 | 900 | Big | 20 |
P.S. Если нам нужен список ФИО, то можно воспользоваться string_agg
:
SELECT s.store_id,
s.name,
(SELECT string_agg (
e.last_name || ' ' || e.first_name, '; '
ORDER BY e.last_name,
e.first_name
)
FROM employee e
WHERE e.store_id = s.store_id
AND e.rank_id = 'MANAGER'
) AS employees
FROM store s
ORDER BY s.store_id
# | store_id | name | employees |
---|---|---|---|
1 | 100 | Пионер | Тарасов Глеб |
2 | 200 | Марс | Калашников Владислав |
3 | 201 | Сатурн | Зайцева Никки; Тимофеева Доминика |
4 | 300 | Адалин | Бирюков Владислав |
5 | 301 | Адалин-family | Емельянова Кристина |
6 | 302 | Адалин-ultra | Кондратьева Марина |
7 | 400 | Европа | Медведев Валерий |
8 | 500 | Март | Картман Эрик |
9 | 600 | Umi | Воробьёв Николай; Калинин Мирон; Кулаков Вилен |
10 | 800 | Универсам | Белозёров Зиновий |
11 | 900 | Big | Авдеев Павел |
Таких функций в PostgreSQL довольно много, и они заслуживают отдельной темы.