Подзапросы

Подзапрос вернул более одной строки

Бывает так, что подзапрос одиночной строки возвращает более одной строки. В таком случае возникнет ошибка.

Для каждого магазина найдем одного сотрудника с должностью '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_idrank_idfull_name
1100MANAGERТарасов Глеб
2200MANAGERКалашников Владислав
3201MANAGERЗайцева Никки
4201MANAGERТимофеева Доминика
5300MANAGERБирюков Владислав
6301MANAGERЕмельянова Кристина
7302MANAGERКондратьева Марина
8400MANAGERМедведев Валерий
9500MANAGERКартман Эрик
10600MANAGERВоробьёв Николай
11600MANAGERКалинин Мирон
12600MANAGERКулаков Вилен
13800MANAGERБелозёров Зиновий
14900MANAGERАвдеев Павел

В магазине 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_idnameemployee_id
1100Пионер11
2200Марс12
3201Сатурн21
4300Адалин13
5301Адалин-family22
6302Адалин-ultra24
7400Европа14
8500Март15
9600Umi16
10800Универсам19
11900Big20

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_idnameemployees
1100ПионерТарасов Глеб
2200МарсКалашников Владислав
3201СатурнЗайцева Никки; Тимофеева Доминика
4300АдалинБирюков Владислав
5301Адалин-familyЕмельянова Кристина
6302Адалин-ultraКондратьева Марина
7400ЕвропаМедведев Валерий
8500МартКартман Эрик
9600UmiВоробьёв Николай; Калинин Мирон; Кулаков Вилен
10800УниверсамБелозёров Зиновий
11900BigАвдеев Павел

Таких функций в PostgreSQL довольно много, и они заслуживают отдельной темы.

ПредыдущаяСледующая