Найти в Дзене
Илья Хохлов

Хитрые решения нетипичных задач с использованием подзапросов в блоке SELECT

В первый же рабочий день нас просит о помощи коллега. Его запрос «почему–то» вызывает ошибку при выполнении: При выполнении данного запроса возникает ошибка: Или, по–русски, эта ошибка отображается так: ORA–01427: подзапрос одиночной строки возвращает более одной строки. Напомню, что пример взят из учебной базы данных курса "SQL, Базы данных. ORACLE". В таблице Persons содержатся Сотрудники, а в таблице PersonCars - их автомобили. В столбце PersonID таблицы PersonCars значения, ссылающиеся на Сотрудников из таблицы Persons. «Что должен возвращать данный запрос?» – спрашиваю я. «Список сотрудников с их автомобилями» – отвечает мой коллега. «Чтобы получить список сотрудников с отображением их автомобилей, не проще ли было бы просто к таблице сотрудников присоединить таблицу автомобилей? – Вновь спрашиваю я – зачем здесь использовать подзапрос?» «Если присоединить к сотрудникам их автомобили, то, при наличии у какого–либо сотрудника нескольких автомобилей, этот сотрудник выведется стольк

В первый же рабочий день нас просит о помощи коллега. Его запрос «почему–то» вызывает ошибку при выполнении:

-2

При выполнении данного запроса возникает ошибка:

-3

Или, по–русски, эта ошибка отображается так: ORA–01427: подзапрос одиночной строки возвращает более одной строки.

Напомню, что пример взят из учебной базы данных курса "SQL, Базы данных. ORACLE". В таблице Persons содержатся Сотрудники, а в таблице PersonCars - их автомобили. В столбце PersonID таблицы PersonCars значения, ссылающиеся на Сотрудников из таблицы Persons.

«Что должен возвращать данный запрос?» – спрашиваю я.

«Список сотрудников с их автомобилями» – отвечает мой коллега.

«Чтобы получить список сотрудников с отображением их автомобилей, не проще ли было бы просто к таблице сотрудников присоединить таблицу автомобилей? – Вновь спрашиваю я – зачем здесь использовать подзапрос?»

«Если присоединить к сотрудникам их автомобили, то, при наличии у какого–либо сотрудника нескольких автомобилей, этот сотрудник выведется столько раз, сколько приджоинилось к нему машин» – отвечает мой коллега, а потом добавляет: «я так уже пробовал».

«Вот в чем дело! Каждого сотрудника нужно вывести только один раз. А тогда, если у него несколько автомобилей, то нужно вывести только одну машину, правильно?» – спрашиваю я – «тогда как мы узнаем какую из его машин выводить?».

«Любую из них, – отвечает мой коллега. – Нужно вывести любую из его машин».

И тут я вспоминаю про агрегатные функции MIN или MAX. С помощью них можно выбрать только одно значение из нескольких. Именно таким «хитрым» способом иногда пользуются аналитики, когда нужно написать подзапрос в блоке SELECT и сделать так, чтобы он возвращал не более одного значения на случай, если подзапросом можно было бы для некоторой строки начитать более одного значения:

-4

В нашем случае, если подзапрос для некоторой строки возвращал бы несколько значений CARNAME, то функция MIN выбрала бы значение, которое идет раньше (по алфавиту).

Как это принято сейчас говорить: «лайф–хак».

«Ура! – Радуется мой коллега, – я пол дня уже с этим мучаюсь. Спасибо тебе!»

И действительно, таким интересным приемом пользуются иногда аналитики, тестировщики, разработчики, когда нужно сделать так, чтобы подзапрос возвращал именно одно значение. Функции MIN и MAX поддерживаются всеми СУБД, и они везде одинаково работают.

Есть и еще один способ решения подобной задачи, без использования агрегатных функций – c использованием ROWNUM. Мы можем во WHERE подзапроса ограничить вывод данных, выведя данные только первой попавшейся строки из тех данных, что начитывал подзапрос:

-5

И получим такой же результат:

-6

Псевдостолбец ROWNUM есть только в ORACLE. В других СУБД подобный вывод только первой строки данных делается немного по–другому.

Если ты работаешь с ORACLE, то смело можешь использовать способ с ROWNUM, так как он более логичный, а если пишешь SQL, который должен запускаться и одинаково правильно работать во всех СУБД, то можно воспользоваться агрегатной функцией MIN или MAX.

На моем опыте такое уже было, что какой–то один разработчик написал запрос по аналогии с MIN(CARNAME), а через какое–то время, другой разработчик не мог понять, почему выбирается именно минимальное значение. Применение MIN или MAX в подобных случаях хорошо бы в SQL–коде комментировать, то есть объяснять.

Буду рад, если подпишешься на мой канал или оставишь комментарий к моему посту!

Заходи на наш Youtube-канал и найдёшь много интересного и полезного про базы данных и язык SQL.

Если у тебя есть Telegram, подписывайся на наш канал. Несколько раз в неделю решаем вместе разные задачи по SQL, и еще помогаю не выпадать из темы айти. Делюсь полезной информацией и техническим-юмором:)

А здесь ты можешь пройти наш курс обучения языку SQL и получить мощные знания и умения! Всего за 27 уроков до уровня специалиста, имеющего трёхлетний стаж. Много практики в реальной базе данных! С нашей поддержкой и проверкой твоих самостоятельный задач! Записывайся, буду тебя ждать!