Даже опытные аналитики регулярно наступают на одни и те же грабли при написании SQL: путают порядок ключевых слов, забывают кавычки, неверно группируют условия в WHERE, ошибаются с форматами дат и типами данных. Большинство таких ошибок не связано с непониманием самой логики выборки — они возникают из-за невнимательности к синтаксису и деталям. Ниже разобраны типичные случаи с примерами «как не надо» и «как правильно». Базовые понятия о реляционных СУБД и языке запросов см. в 07.01 Базы данных и SQL на уровне аналитика.

1. Неправильный порядок ключевых слов

Самая частая ошибка — нарушение порядка следования основных конструкций запроса. В SQL предложения должны идти строго в определённой последовательности: SELECT → FROM → WHERE → ORDER BY.

Ошибочный вариант, где WHERE оказался перед FROM:

select * where range > '5000'
from aircrafts_data
order by range

Правильный вариант — сначала источник данных, затем условие фильтрации:

SELECT *
FROM aircrafts_data
WHERE range > '5000'
ORDER BY range;

Для сортировки по убыванию используется DESC после ORDER BY (по умолчанию действует возрастающий порядок ASC):

SELECT *
FROM aircrafts_data
WHERE range > 5000
ORDER BY range DESC;

2. Ошибки при работе с JSONB

При извлечении данных из полей типа JSONB важно учитывать структуру объекта. В примере ниже сразу несколько проблем: неверный порядок WHERE и FROM, а также некорректное условие фильтрации по JSONB-полю.

SELECT aircraft_code, model->>'en' AS aircraft_name
FROM aircrafts_data
WHERE model->>'manufacturer' = 'Boeing'
ORDER BY aircraft_code;

Ключевые моменты:

  • FROM должен идти перед WHERE;
  • для извлечения значения из JSONB используется оператор ->>, и условие фильтрации тоже должно обращаться к нужному ключу внутри объекта;
  • предполагается, что поле model содержит ключ manufacturer.

3. Типы данных и форматы даты/времени

Отдельная категория ошибок связана с несоответствием типов данных и форматов. Если столбец имеет тип DATE или DATETIME, применение оператора LIKE со строковым шаблоном неоптимально — надёжнее использовать диапазоны дат или функции извлечения даты.

Некорректный формат timestamp

Типичная ошибка PostgreSQL — лишние символы в строке даты. Формат '2017-08-01 00:00:00:00' (лишний ноль в конце) приводит к ошибке:

ERROR: invalid input syntax for type timestamp with time zone:
"2017-08-01 00:00:00:00"

Ожидаемый формат для timestamp with time zone — 'YYYY-MM-DD HH:MI:SS'. Исправленный запрос:

SELECT *
FROM flights
WHERE departure_airport = 'DME'
AND actual_departure > '2017-08-01 00:00:00'
ORDER BY actual_departure DESC;

Фильтрация по конкретному дню

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

SELECT *
FROM flights
WHERE departure_airport = 'DME'
AND actual_departure >= '2017-08-01 00:00:00'
AND actual_departure < '2017-08-02 00:00:00'
ORDER BY actual_departure ASC;

4. Соответствие запроса заданию

Помимо синтаксической корректности важно проверять, что логика запроса отвечает поставленной задаче. Например, для задания «получить список рейсов, фактически вылетевших из аэропорта Домодедово (DME) 1 августа 2017 г., отсортированных по фактическому времени вылета» запрос выше удовлетворяет всем критериям:

  • фильтрация по аэропорту вылета (departure_airport = 'DME');
  • ограничение по дате через диапазон >= '2017-08-01' и < '2017-08-02';
  • сортировка по actual_departure.

5. Логика условий в WHERE: запятые, алиасы и приоритет операторов

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

SELECT * FROM player
Where p.age > 65, p.status = alive, debt > 400000000 OR vice = Gambling AND has_close_family = 0;

Здесь сразу несколько проблем:

  1. Запятые в условии WHERE — для объединения условий нужны логические операторы AND или OR, а не запятые.
  2. Неопределённый алиас p — алиас используется в p.age, но не объявлен в секции FROM. Его нужно указать после имени таблицы.
  3. Отсутствие кавычек у строковых значений — alive и Gambling должны быть заключены в одинарные кавычки.
  4. Приоритет логических операторов — без скобок AND и OR могут интерпретироваться не так, как задумано. Приоритет следует задавать явно.
  5. Формат числового значения — значение долга должно соответствовать типу данных столбца debt.

Исправленный вариант:

SELECT *
FROM player p
WHERE p.status = 'alive'
  AND p.debt > 400000000
  AND (
    p.age > 65
    OR (p.vice = 'Gambling' AND p.has_close_family = 0)
  );

Здесь алиас p определён сразу после имени таблицы, строковые значения взяты в кавычки, запятые заменены логическими операторами, а скобки группируют условия так, чтобы OR применялся только к нужной части выражения.

6. Ошибки при изменении структуры таблиц (ALTER TABLE)

Отдельная категория проблем связана с командой ALTER TABLE, которая изменяет структуру существующей таблицы. Типичная ошибка — при добавлении колонки не указывается её тип данных:

Can't create or update target table:
ALTER TABLE portfolio.yearly_buy ADD "3ffa6dde-e43c-4078-b48c-816455d77369"

Возможные причины:

  1. Ошибка синтаксиса — в команде ALTER TABLE ... ADD необходимо указать имя колонки и её тип данных. В примере указано только значение, похожее на UUID, без типа.
  2. Некорректное имя колонки — использование UUID в качестве имени технически возможно, но крайне не рекомендуется: это усложняет дальнейшую работу с таблицей.
  3. Недостаточно прав — возможно, у пользователя нет прав на изменение структуры таблицы или схемы.

Корректный синтаксис:

ALTER TABLE portfolio.yearly_buy ADD transaction_id VARCHAR(36);

7. Ошибки соединения с СУБД

Помимо синтаксических ошибок в самих запросах, встречаются проблемы на уровне соединения с СУБД. Пример сообщения при подключении к ClickHouse:

ClickHouse exception, code: 210, host: mesprtf-chp1p, port: 9999;
Connect to mesprtf-chp1p:9999 [mesprtf-chp1p/10.15.99.153] failed: Connection refused

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

8. Итоговая памятка

Категория ошибкиЧто проверить
Порядок ключевых словSELECT → FROM → WHERE → ORDER BY
JSONBОператор ->>, обращение к нужному ключу, порядок секций
Даты и времяФормат 'YYYY-MM-DD HH:MI:SS', диапазон вместо одной метки
Составные условияAND/OR вместо запятых, кавычки, скобки, алиасы
ALTER TABLEИмя колонки и её тип данных, права на изменение
Соединение с СУБДДоступность хоста, порт, состояние сервиса

Общий принцип: большинство «загадочных» ошибок SQL — это либо нарушение синтаксиса, либо несоответствие типов, либо неявный приоритет операторов. Прежде чем менять логику запроса, стоит внимательно перечитать его структуру и сверить её с требованиями задания.