JavaRush /Курсы /SQL SELF /Типичные ошибки при работе с подзапросами

Типичные ошибки при работе с подзапросами

SQL SELF
14 уровень , 4 лекция
Открыта

Работа с подзапросами — это как игра в шахматы на раздевание: всё кажется простым на первый взгляд, пока вы не сделаете ход с ошибкой. А откуда появляются ошибки? Из недопонимания синтаксиса, игнорирования особенностей логики SQL или просто невнимательности. В этой лекции мы поговорим о наиболее распространённых ошибках, а также о том, как их избежать.

Ошибки синтаксиса

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

Пропущенные скобки

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

Пример ошибки:

SELECT student_name
FROM students
WHERE student_id IN SELECT student_id FROM enrollments);

Ошибка:

ERROR:  syntax error at or near "SELECT"

Исправление:

SELECT student_name
FROM students
WHERE student_id IN (SELECT student_id FROM enrollments);

Комментарий: Запрос в IN всегда должен быть заключён в скобки, чтобы SQL понимал, что вы хотите выполнить подзапрос.

Отсутствие алиасов

Когда вы используете подзапросы в секции FROM, не забудьте дать им алиас. Без него PostgreSQL просто запутается.

Пример ошибки:

SELECT student_name, avg_score
FROM (SELECT student_id, AVG(score) AS avg_score FROM grades GROUP BY student_id)
WHERE avg_score > 80;

Ошибка:

ERROR:  subquery in FROM must have an alias

Исправление:

SELECT student_name, avg_score
FROM (SELECT student_id, AVG(score) AS avg_score FROM grades GROUP BY student_id) AS subquery
WHERE avg_score > 80;

Комментарий: PostgreSQL требует, чтобы каждая временная таблица (результат подзапроса в FROM) имела имя.

Проблемы с производительностью

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

Избыточные вычисления

Подзапросы в секции SELECT могут вычисляться для каждой строки результата, что ведет к огромным затратам времени.

Пример:

SELECT student_name,
       (SELECT COUNT(*) FROM enrollments WHERE enrollments.student_id = students.student_id) AS course_count
FROM students;

Если в таблице students десятки тысяч строк, этот подзапрос будет выполнен для каждой строки заново.

Оптимизация:

WITH course_counts AS (
    SELECT student_id, COUNT(*) AS course_count
    FROM enrollments
    GROUP BY student_id
)
SELECT s.student_name, c.course_count
FROM students s
LEFT JOIN course_counts c ON s.student_id = c.student_id;

CTE (Common Table Expression) или джойны позволяют избежать перерасчёта для каждой строки. Этого подхода мы коснемся через пару уровней :P

Отсутствие индексов

Если вы используете сложные подзапросы в секции WHERE, убедитесь, что нужные столбцы индексированы.

Пример:

SELECT student_name
FROM students
WHERE student_id IN (SELECT student_id FROM enrollments WHERE course_id = 10);

Если на столбце student_id в таблице enrollments нет индекса, подзапрос будет выполнен через полное сканирование таблицы.

Оптимизация: Создайте индекс:

CREATE INDEX idx_enrollments_course_id ON enrollments (course_id);

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

Ошибки логики

Ошибки логики в подзапросах встречаются не реже синтаксических. Проблемы могут возникнуть из-за неправильного понимания работы NULL, фильтров или агрегатных функций.

Неправильное использование NULL

NULL — это ловушка, которая поджидает каждого новичка. При использовании IN или NOT IN в подзапросах наличие NULL может повлиять на результат.

Пример ошибки:

SELECT student_name
FROM students
WHERE student_id NOT IN (SELECT student_id FROM enrollments);

Если в таблице enrollments есть строки с student_id = NULL, запрос ничего не вернет. Это связано с тем, что условие NOT IN срабатывает как NULL IS NOT IN.

Исправление:

SELECT student_name
FROM students
WHERE student_id NOT IN (SELECT student_id FROM enrollments WHERE student_id IS NOT NULL);

Всегда фильтруйте NULL, если используете NOT IN.

Ошибки в условиях фильтрации

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

Пример ошибки:

SELECT student_name
FROM students
WHERE (SELECT AVG(score) FROM grades WHERE grades.student_id = students.student_id) > 80;

Если хотя бы у одного студента нет оценок, подзапрос вернёт NULL, и студент не попадёт в результат.

Исправление:

SELECT student_name
FROM students
WHERE COALESCE((SELECT AVG(score) FROM grades WHERE grades.student_id = students.student_id), 0) > 80;

Используйте COALESCE, чтобы заменить NULL значениями по умолчанию.

Рекомендации по предотвращению ошибок

Чтобы избежать типичных ошибок при работе с подзапросами, придерживайтесь следующих правил:

Правильное использование скобок и алиасов. Если что-то не работает, проверьте, закрыты ли все скобки и указаны ли алиасы в подзапросах.

Оптимизация запросов. Постарайтесь минимизировать использование подзапросов там, где можно применить JOIN, WITH или индексы.

Учет NULL. Всегда учитывайте возможность появления NULL в подзапросах и используйте IS NOT NULL, COALESCE или аналогичные конструкции.

Тестирование. Тестируйте каждый подзапрос отдельно, чтобы убедиться, что он возвращает нужный результат.

Читаемость. Используйте отступы и алиасы, чтобы ваш код был читаемым. Помните: через месяц вы сами можете не вспомнить, что написали.

2
Задача
SQL SELF, 14 уровень, 4 лекция
Недоступна
Правильное использование скобок и алиасов
Правильное использование скобок и алиасов
2
Задача
SQL SELF, 14 уровень, 4 лекция
Недоступна
Учет NULL в подзапросах
Учет NULL в подзапросах
1
Опрос
Использование подзапросов, 14 уровень, 4 лекция
Недоступен
Использование подзапросов
Использование подзапросов
Комментарии (8)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Slevin Уровень 11
6 сентября 2025
Тест: Какой оператор используется в HAVING для фильтрации агрегированных данных? Правильного ответа нет. Есть псевдо-правильный. Какой метод лучше использовать вместо подзапроса для агрегации данных? Правильный ответ мы не проходили и потому не знаем. Что произойдёт, если в блоке HAVING использовать условие с NULL? Может быть покажете КАК использовать? От этого как бы несколько поменяется ответ, а? Какие подзапросы могут быть заменены JOIN для повышения производительности? Ответ "Подзапросы в SELECT НЕ(!!!) подошел СРОЧНО ПРИГЛАСИТЕ КОЖАННОГО ДЛЯ ПРОВЕРКИ ТЕСТА!!! 😱😱😱
Slevin Уровень 11
6 сентября 2025
Я вижу главную проблему лекций по PostgreSQL на этом сайте в том, что авторы не решили - они тут пишут лекции по PostgreSQL или просто по SQL - отсюда и постоянные проблемы с этими NULL, которых в Postgre просто не существует… Нет, я понимаю, что лекции написаны ИИ и не прочитаны ни разу (там даже оформление страдает неоднородностью, я это замечаю, когда копирую блоки лекции в конспект в Obsidian. В общем, небольшие усилия по вычитке лекций, удалению дублирования материала и т.п. - сделали бы курс лучше сразу процентов эдак на 80... Та же история была с курсом по Python. Где отдельные лекции, видно что написаны людьми, а отдельные - просто треш.
Sergii Kononenko Уровень 16
19 августа 2025
student_name - отсутствует в подзапросе. Так не работает с реальной БД. Не ожидал такого огромного количества ошибок. Курс - гомно-качество.
Alexandr Уровень 16
8 июля 2025
Какие подзапросы могут быть заменены JOIN для повышения производительности? И опять надо гадать, что там думал автор так как вариантов ответа может быть очень много.
Ra Уровень 35 Student
29 июля 2025
По моему, только 1 вариант подходит, разве нет?
Евгений Уровень 49 Expert
30 июля 2025
Разве это не подзапросы в SELECT? Но это неправильный вариант ответа.
Евгений Уровень 49 Expert
30 июля 2025
Да, конкретно ранее упоминалось, что можно заменить подзапрос в FROM на JOIN, но так вроде бы лучше заменить вообще всё перечисленное.
Slevin Уровень 11
6 сентября 2025
Из того что я понял общаясь с ЧатомГПТ: - подзапрос в SELECT - самая шляпа, он будет выполнять выборку для КАЖДОЙ СТРОКИ. - подзапрос во FROM - уже чуть получше и в зависимости от кода может быть выполнен в принципе один раз, но если есть какие-то агрегирующие функции, группировки - то будет выполняться для КАЖДОЙ ГРУППЫ. Как-то так я это понял.