Pull to refresh

Ошибки, которые можно избежать в SQL: грабли начинающего аналитика

Level of difficultyEasy
Reading time5 min
Views2.2K

SQL — мощная штука, но даже простые запросы могут выдать не верный результат, если не знать подводных камней. В этой статье — список типовых ошибок, на которые чаще всего наступают новички. Всё объясняю на живых примерах с PostgreSQL.

Какие бывают ошибки в SQL

Ошибки в SQL можно условно разделить на несколько категорий:

  1. Синтаксические ошибки. Это ошибки в написании SQL-кода: пропущенные запятые, неверные ключевые слова, неправильный порядок конструкции. Они чаще всего ловятся самим движком базы при попытке выполнить запрос.

  2. Логические ошибки. Самые коварные. Код выполняется, но результат не тот. Например, неверный фильтр, JOIN по неправильному полю, перепутанный порядок WHERE и HAVING или лишний DISTINCT. Эти ошибки особенно опасны в аналитике, потому что могут привести к неверным бизнес-решениям.

  3. Ошибки работы с NULL. NULL — это отдельная категория значений в SQL, и она требует особого внимания. Сравнение через = и != с NULL не работает так, как многие ожидают. Здесь нужны IS NULL и IS NOT NULL.

  4. Ошибки при работе с JOIN. Отсутствие условия соединения, неправильный тип соединения (INNER вместо LEFT, или наоборот), дублирование строк из-за некорректного связывания — всё это может нарушить итоговую выборку.

  5. Ошибки производительности. Использование SELECT * в больших таблицах, отсутствие индексов на полях фильтрации, тяжёлые подзапросы и вложенные SELECT’ы там, где можно обойтись CTE или JOIN — всё это тормозит выполнение и грузит сервер.

  6. Ошибки доступа. Запрос к несуществующей таблице, попытка обращения к колонке с опечаткой, отсутствие прав на 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-канале. Подписывайтесь и читайте контент по ссылке.

Tags:
Hubs:
-1
Comments9

Articles