В блог
10 SQL-ошибок и фишек, о которых редко говорят - Симулейтив

10 SQL-ошибок и фишек, о которых редко говорят

Дата последнего обновления: 30.05.2026
Дата размещения: 30.05.2026
Евгений Буторин
Руководитель отдела CRM-аналитики развития клиентской базы в Альфа Банке

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

1. COUNT(*) vs COUNT(col)

Многие уверены, что COUNT(*), COUNT(1) и COUNT(имя_столбца) — одно и то же. Но это не так.

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

SELECT COUNT(email) FROM users

то получите количество заполненных имейлов. То есть если у пользователя email равен NULL, он не попадёт в подсчёт. А вот:

SELECT COUNT(*) FROM users

или

SELECT COUNT(1) FROM users

посчитают все строки.

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

2. Лишние поля в GROUP BY

Например, у вас есть таблица с транзакциями клиентов, в которой есть ID клиента, ID транзакции и её размер. Вам нужно считать общее количество транзакций на клиента.

Новички часто не понимают, как работает GROUP BY и какие поля в нём указывать, поэтому часто можно встретить такое:

SELECT client_id, sum(amount) as sum_amount
FROM transactions
GROUP BY client_id, transact_id

Данный скрипт отработает, но вместо одного client_id у вас будет 10 строк (зависит от количества транзакций у клиента). Для того чтобы написать правильно, нужно указывать только те поля, которые есть в SELECT:

SELECT client_id, sum(amount) as sum_amount
FROM transactions
GROUP BY client_id

3. Неверные поля в GROUP BY

Ещё одной частой ошибкой при использовании GROUP BY является использование поля вместо условия. Например:

SELECT client_id, case when start_date >= date2026-01-01then 1 else 0 end as new_client_flag, sum(amount) as sum_amount
FROM transactions
GROUP BY client_id, start_date

Этот скрипт отработает, но сгруппирует неверно. Чтобы правильно сгруппировать строки, нужно использовать всё условие:

SELECT client_id, case when start_date >= date2026-01-01then 1 else 0 end as new_client_flag, sum(amount) as sum_amount
FROM transactions
GROUP BY client_id, case when start_date >= date2026-01-01then 1 else 0 end

4. LIKE без %

На работе нам часто приходится искать данные в текстовых полях. Например, мы отправляем SMS клиентам, но предварительно тестируем их. Заказчик просит нас собрать воронку, поэтому нам нужно исключить тестовые записи.

Если мы напишем:

WHERE name NOT LIKE ‘test’

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

WHERE name NOT LIKE%test%

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

5. DISTINCT не лечит дубли

Очень частая ошибка: «У меня дубли, добавлю DISTINCT и всё». Но DISTINCT — это не всегда решение. Важно понимать причину дублей и использовать соответствующий метод дедупликации.

Например:

SELECT DISTINCT u.client_id, t.amount
FROM clients u
LEFT JOIN transactions t ON u.client_id = t.client_id

Если у пользователя 10 транзакций, вы получите 10 строк. DISTINCT оставит 10 строк, потому что amount разный.

Правильный подход — агрегировать, суммируя amount:

SELECT u.client_id, SUM(t.amount) AS total_amount
FROM clients u
LEFT JOIN transactions t ON u.client_id = t.client_id
GROUP BY u.client_id

6. Использование SELECT * в продовых запросах

SELECT * — это самый частый запрос, который я пишу, но не всё так просто. Да, на этапе исследования данных это очень удобно. Ты видишь все столбцы и понимаешь структуру таблицы, но в проде SELECT * — это лишние данные, которые нагружают темп и память.

Например, у нас есть таблица с пользователями и транзакциями:

SELECT *
FROM transactions t
JOIN users u ON t.client_id = u.client_id

Сделать SELECT * для изучения и проверки — правильное решение, но записывать такую конструкцию в созданную таблицу нельзя. Если в таблицу users добавят поле с большим текстом — например, выводы ИИ о клиенте — то во-первых, запрос сломается, так как появится новое поле. А во-вторых, если заменили содержимое существующего поля, то запрос внезапно станет в 10 раз тяжелее.

Правильно отбирать только нужные столбцы:

SELECT t.client_id, t.amount, u.segment
FROM transactions t
JOIN users u ON t.client_id = u.client_id

7. Неправильная работа с NULL в условиях

NULL — это не значение, это пустота. И многие забывают, что сравнения с NULL работают иначе, чем сравнение с заполненными полями.

Например, вам нужно отобрать всех клиентов, кроме клиентов с тестовым емейлом. Если написать:

WHERE email <> ‘test@example.com’

То строки, где email = NULL, не попадут в выборку, хотя логически должны. Это частая ошибка, на которой ловят новичков. Чтобы правильно отобрать клиентов, используйте:

WHERE email <> ‘test@example.com’ OR email IS NULL

или

WHERE nvl(email, ‘N/A’) <> ‘test@example.com’

8. Фильтрация после JOIN вместо фильтрации до JOIN

Очень часто нам нужно объединить таблицы и отфильтровать их одновременно. Многие начинающие аналитики делают так:

SELECT *
FROM users u
LEFT JOIN transactions t ON u.client_id = t.client_id
WHERE t.amount > 100

Фильтр превращает LEFT JOIN в INNER JOIN и убивает значения из таблицы users, так как отфильтровывает все данные после объединения. Но пользователи без транзакций вам тоже нужны. Чтобы сделать правильный запрос и ничего не потерять, используйте фильтрацию внутри JOIN:

LEFT JOIN transactions t
    ON u.client_id = t.client_id
    AND t.amount > 100

Это не только правильно, но и оптимально с точки зрения производительности.

9. Использование HAVING вместо WHERE

Путаница между WHERE и HAVING — это вечная проблема. Запомните, HAVING — это фильтр после группировки. Давайте рассмотрим на примере:

SELECT employee_id, SUM(salary)
FROM salary
GROUP BY employee_id
HAVING SUM(salary) > 1000

В данном запросе результат будет покажет только сотрудников с общей зарплатой более 1000 за весь период. То есть фильтрация будет после агрегации. Но если вам нужны только те сотрудники, которые каждый месяц получают более 1000, то есть условие до агрегации, то вам нужно использовать:

SELECT employee_id, SUM(salary)
FROM salary
GROUP BY employee_id
WHERE salary > 1000

Выглядит похоже, но смысл совершенно разный.

10. Использование UNION вместо UNION ALL

UNION, в отличие от UNION ALL, сверяет все строки и удаляет дубли. Поэтому использование UNION значительно замедляет запрос. Если вам не нужно удалять дубли, то не пишите:

SELECT client_id FROM table1
UNION
SELECT client_id FROM table2

Вместо этого используйте:

SELECT client_id FROM table1
UNION ALL
SELECT client_id FROM table2
Подпишитесь на нашу рассылку
Имя*
Email*
Номер телефона*
Заполняя данную форму, Вы соглашаетесь с политикой конфиденциальности
Никакого спама. Только точечные рассылки с лучшими материалами.