Ошибка ORA-01427: single-row subquery returns more than one row
- Ошибка
ORA-01427: single-row subquery returns more than one rowпри выполнении SQL-запросов (SELECT,UPDATE,DELETE). - Скалярный подзапрос (в секции
WHERE col = (...)илиSELECT (...) FROM ...) возвращает множественные строки вместо одной. - Сбои тяжелых аналитических отчетов и интеграционных выгрузок.
1. Определение некорректного подзапроса
Ошибка возникает, когда оператор скалярного сравнения (=, !=, >, <) ожидает ровно одно значение:
-- Ошибочный запрос (если у одного клиента несколько заказов):
SELECT client_name,
(SELECT order_id FROM orders o WHERE o.client_id = c.id) AS order_id
FROM clients c;2. Способ исправления А: Замена оператора на IN или ANY
-- Вместо WHERE col = (SELECT ...)
SELECT * FROM clients
WHERE id IN (SELECT client_id FROM orders WHERE total > 1000);3. Способ исправления Б: Использование агрегации или ограничения выборки
-- Получение последнего заказа через агрегацию:
SELECT client_name,
(SELECT MAX(order_id) FROM orders o WHERE o.client_id = c.id) AS last_order_id
FROM clients c;4. Способ исправления В: Использование аналитических функций (ROW_NUMBER)
WITH ranked_orders AS (
SELECT client_id, order_id,
ROW_NUMBER() OVER (PARTITION BY client_id ORDER BY order_date DESC) as rn
FROM orders
)
SELECT c.client_name, ro.order_id
FROM clients c
LEFT JOIN ranked_orders ro ON c.id = ro.client_id AND ro.rn = 1; Частые вопросы (FAQ)
Чем ORA-01427 отличается от ORA-01422?
ORA-01422 генерируется в PL/SQL при выполнении команды SELECT INTO. ORA-01427 генерируется SQL-движком при выполнении обычного SQL-запроса, содержащего скалярный подзапрос.
Почему ORA-01427 может не возникать на тестовой базе, но падать в Production?
На тестовой базе в таблицах мало данных (или нет дубликатов по ключам). На проде при росте объема данных подзапрос начинает возвращать 2+ строки, что приводит к внезапному сбою.
Как переписать UPDATE с подзапросом, чтобы избежать ORA-01427?
Используйте конструкцию MERGE INTO. Она позволяет прозрачно управлять связыванием наборов данных без риска падения от некорректных подзапросов в блоке SET.
Помогает ли ключевое слово ROWNUM = 1 внутри подзапроса?
Да, добавление WHERE ROWNUM = 1 гарантирует возврат не более одной строки, однако без правильного ORDER BY возвращенное значение может оказаться непредсказуемым.