
SQL — мощная штука, но даже простые запросы могут выдать не верный результат, если не знать подводных камней. В этой статье — список типовых ошибок, на которые чаще всего наступают новички. Всё объясняю на живых примерах с PostgreSQL.
Какие бывают ошибки в SQL
Ошибки в SQL можно условно разделить на несколько категорий:
-
Синтаксические ошибки. Это ошибки в написании SQL-кода: пропущенные запятые, неверные ключевые слова, неправильный порядок конструкции. Они чаще всего ловятся самим движком базы при попытке выполнить запрос.
-
Логические ошибки. Самые коварные. Код выполняется, но результат не тот. Например, неверный фильтр, JOIN по неправильному полю, перепутанный порядок WHERE и HAVING или лишний DISTINCT. Эти ошибки особенно опасны в аналитике, потому что могут привести к неверным бизнес-решениям.
-
Ошибки работы с NULL. NULL — это отдельная категория значений в SQL, и она требует особого внимания. Сравнение через = и != с NULL не работает так, как многие ожидают. Здесь нужны IS NULL и IS NOT NULL.
-
Ошибки при работе с JOIN. Отсутствие условия соединения, неправильный тип соединения (INNER вместо LEFT, или наоборот), дублирование строк из-за некорректного связывания — всё это может нарушить итоговую выборку.
-
Ошибки производительности. Использование SELECT * в больших таблицах, отсутствие индексов на полях фильтрации, тяжёлые подзапросы и вложенные SELECT’ы там, где можно обойтись CTE или JOIN — всё это тормозит выполнение и грузит сервер.
-
Ошибки доступа. Запрос к несуществующей таблице, попытка обращения к колонке с опечаткой, отсутствие прав на SELECT/INSERT — это технические ошибки, но тоже распространённые. Часто возникают при смене окружения (dev → prod, другой пользователь и т.д.).
Перейдем к примерам распространенных ошибок.
Синтаксическая ошибка с некорректным GROUP BY
Ошибка возникает, если указать в SELECT столбцы, которые не попадают ни в агрегатную функцию, ни в GROUP BY.
Ошибочный запрос, который выдаст ошибку “ERROR: column “sales.product” должен присутствовать в предложении GROUP BY или использоваться в агрегатной функции:
SELECT customer_id, product, amount
FROM sales
GROUP BY customer_id;
Исправленный запрос:
SELECT customer_id, SUM(amount) AS total_amount
FROM sales
GROUP BY customer_id;
Логическая ошибка при использовании DISTINCT с агрегатной функцией без GROUP BY
Комбинация DISTINCT и агрегатных функций, таких как SUM, AVG, COUNT, без явного указания GROUP BY, вводит SQL в замешательство. Запрос неясен: нужно ли агрегировать по customer_id, или просто выбрать уникальные строки? SQL требует однозначности — все неагрегированные поля в SELECT должны быть указаны в GROUP BY. Иначе возникает ошибка выполнения.
Ошибочный запрос:
SELECT DISTINCT customer_id, SUM(amount)
FROM sales;
Результатом будет:
ERROR: column “sales.customer_id” must appear in the GROUP BY clause or be used in an aggregate function
Исправленный запрос:
SELECT customer_id, SUM(amount) AS total_amount
FROM sales
GROUP BY customer_id;
Ошибка при использовании JOIN с несовместимыми типами данных
При соединении таблиц через поля с разными типами данных (INTEGER, TEXT, UUID, и т. д.) база данных может не только вернуть некорректные результаты, но и вовсе не выполнить соединение. Особенно это критично, если соединение происходит по полям с разной длиной или форматом — ошибки при этом могут быть скрытыми и долго не обнаруживаться.
Ошибочный запрос:
SELECT o.order_id, c.customer_name
FROM orders o
JOIN customers c ON o.order_id = c.customer_id;
Запрос будет ошибочным, если order_id и customer_id имеют разные типы данных.
Исправленный запрос:
SELECT o.order_id, c.customer_name
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id;
Удаление данных из таблицы без WHERE
Это классика жанра: забыть WHERE в DELETE — всё равно что взять и нажать «Удалить всё», и подтвердить. Вместо удаления пары строк исчезает вся таблица. Особенно больно, если это прод и нет бэкапа. Один неосторожный DELETE, и ваша база превращается в чистый лист.
Ошибочный запрос, который удалит все строки из таблицы:
DELETE FROM products;
Исправленный запрос:
DELETE FROM products
WHERE category_id = 50 AND product_name = 'Pear';
Ошибка работы с NULL при сравнении через =
NULL в SQL — это не просто «пусто», а «неизвестно». А с неизвестным нельзя сравнивать напрямую. Условие amount = NULL никогда не даст TRUE, потому что результат сравнения с NULL — всегда NULL, то есть «неизвестно». Поэтому такой запрос не вернёт ни одной строки, даже если NULL в колонке есть. Для проверки нужно использовать IS NULL и IS NOT NULL.
Ошибочный запрос, который не выдаст ни одной строки:
SELECT *
FROM sales
WHERE amount = NULL;
Исправленный запрос:
SELECT *
FROM sales
WHERE amount IS NULL;
Ошибка с JOIN без условий соединения
Забыть ON в JOIN — всё равно что сказать базе: «Соедини всё со всем, как хочешь». В первом случае вы получите синтаксическую ошибку, а во втором — декартово произведение: каждая строка из первой таблицы будет соединена с каждой строкой из второй. Это быстро превращает обычный запрос в лавину данных и боль для сервера (и аналитика).
Ошибочный запрос с синтаксической ошибкой:
SELECT *
FROM sales
JOIN customers;
Ошибочный запрос с декартовым произведением:
SELECT *
FROM sales
JOIN customers ON 1=1;
Исправленный запрос:
SELECT s.*, c.name
FROM sales s
JOIN customers c ON s.customer_id = c.id;
Неправильный порядок условий в WHERE
Новички часто забывают про приоритет логических операторов AND и OR, из-за чего запрос работает не так, как задумывалось.
Ошибочный запрос:
SELECT * FROM orders
WHERE status = 'completed' OR status = 'paid' AND delivery_date IS NOT NULL;
Этот запрос вернёт ВСЕ completed и только те paid, у которых указана дата доставки. Не то, что хотели.
Исправленный запрос:
SELECT * FROM orders
WHERE (status = 'completed' OR status = 'paid')
AND delivery_date IS NOT NULL;
Скобки меняют порядок выполнения и делают логику запроса правильной и понятной.
Неоправданное использование подзапросов
Подзапросы внутри SELECT могут выглядеть удобно, но часто создают лишнюю нагрузку. Каждый подзапрос выполняется отдельно для каждой строки — а это значит больше вычислений, больше времени и меньше масштабируемости. Там, где можно использовать JOIN, лучше так и сделать: это быстрее и понятнее.
Ошибочный запрос:
SELECT
order_id,
(SELECT customer_name FROM customers WHERE customer_id = orders.customer_id)
FROM orders;
Исправленный запрос:
SELECT o.order_id, c.customer_name
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id;
Ошибка производительности при выборе всех строк с SELECT *
Можно воспринимать эту ошибку как «принеси мне всё из холодильника, хотя я хотел только яблоко». Такой запрос тянет все колонки, включая те, которые не нужны. Это замедляет выполнение, особенно при работе с большими таблицами, и мешает оптимизатору строить эффективный план.
Ошибочный запрос:
SELECT * FROM customers;
Исправленный запрос:
SELECT id, name FROM customers;
Отсутствие WHERE и LIMIT при больших данных в таблице
Без WHERE и LIMIT вы загружаете всю таблицу — даже если вам нужно 5 строк. Это всё равно что выгружать весь архив почты за 10 лет, чтобы найти одно письмо. Такой запрос сильно нагружает базу, тормозит интерфейс и может привести к таймаутам или сбоям.
Ошибочный запрос:
SELECT * FROM large_table
Исправленный запрос:
SELECT * FROM large_table
WHERE created_at > NOW() - INTERVAL '7 days'
LIMIT 1000;
Неэффективное использование OR
Запросы с множеством OR выглядят безобидно, но могут мешать оптимизатору построить эффективный план выполнения. Особенно в больших таблицах это замедляет работу. Использование IN делает запрос компактнее, читаемее и зачастую быстрее.
Ошибочный запрос:
SELECT *
FROM orders
WHERE customer_id = 1 OR customer_id = 2;
Исправленный запрос:
SELECT *
FROM orders
WHERE customer_id IN (1, 2);
Заключение
Большинство ошибок новичков — это результат непонимания порядка выполнения SQL-запроса. Разбирайте, в какой момент работает WHERE, когда применяется GROUP BY, и когда можно использовать алиасы. Лучше медленно, но правильно, чем быстро и с багами. SQL прощает мало, но учит быстро — особенно если смотреть на результаты собственных запросов внимательно.
Не бойтесь экспериментировать, но всегда проверяйте себя: читайте, что именно возвращает ваш запрос, и задавайте себе вопрос: «А это точно то, что я хотел(а) получить?» Чем раньше вы привыкнете анализировать не только код, но и его поведение, тем быстрее исчезнет ощущение, что SQL — это какая-то магия. На самом деле, это просто строгое, но честное ремесло.
Еще больше полезных материалов в моем TG-канале. Подписывайтесь и читайте контент по ссылке.
Автор: Alena_Les
