Почему SQL возвращает пустой результат
Карьерник — квиз-тренажёр в Telegram с 1500+ вопросами для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.
Основные причины
- Условие в WHERE слишком строгое (опечатка, неверное значение)
- NULL в сравнении —
column = NULLвсегда FALSE - INNER JOIN убрал строки без пары
- Регистр —
'Alice' ≠ 'alice' - Пробелы / trim —
'Alice' ≠ 'Alice ' - Неверная таймзона или границы даты
- NOT IN с NULL — возвращает пусто
- HAVING после WHERE фильтрует ещё раз
1. Опечатки в значениях
-- пусто, потому что в базе 'Moscow', а не 'Moskow'
SELECT * FROM users WHERE city = 'Moskow';
-- проверка
SELECT DISTINCT city FROM users ORDER BY city LIMIT 20;2. NULL в сравнении
-- всегда FALSE, даже если есть строки с NULL
SELECT * FROM users WHERE phone = NULL;
-- правильно
SELECT * FROM users WHERE phone IS NULL;Почему? Потому что NULL = NULL в SQL даёт UNKNOWN, а не TRUE.
3. INNER JOIN срезает строки
-- пользователи, у которых НЕТ заказов, выпадут
SELECT u.name, o.total
FROM users u
INNER JOIN orders o ON o.user_id = u.id;Если ожидали увидеть всех пользователей → LEFT JOIN:
SELECT u.name, o.total
FROM users u
LEFT JOIN orders o ON o.user_id = u.id;4. Условие в WHERE ломает LEFT JOIN
-- превращает LEFT в INNER
SELECT u.*, o.status
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
WHERE o.status = 'paid';
-- правильно: условие в ON
SELECT u.*, o.status
FROM users u
LEFT JOIN orders o ON o.user_id = u.id AND o.status = 'paid';Разница: в WHERE — фильтр после JOIN (убирает NULL-строки). В ON — фильтр во время JOIN.
5. Регистр
-- возможно, в БД 'Alice', не 'alice'
SELECT * FROM users WHERE name = 'alice';
-- регистронезависимо
SELECT * FROM users WHERE LOWER(name) = LOWER('alice');
-- Postgres
SELECT * FROM users WHERE name ILIKE 'alice';6. Пробелы
-- в БД "Alice " с пробелом на конце
SELECT * FROM users WHERE name = 'Alice'; -- пусто
-- с trim
SELECT * FROM users WHERE TRIM(name) = 'Alice';Лучше почистить данные в ETL, а не каждый раз TRIM.
7. Таймзоны
-- сервер UTC, а вы думаете, что Москва
SELECT * FROM events
WHERE created_at >= '2026-04-21';
-- захватывает события с 21 апреля 00:00 UTC = 03:00 МСК
-- нужна явная таймзона
SELECT * FROM events
WHERE created_at >= '2026-04-21 00:00:00 Europe/Moscow';8. Границы даты
-- не включает 2026-04-30 23:59
SELECT * FROM orders
WHERE created_at BETWEEN '2026-04-01' AND '2026-04-30';
-- правильно: полуоткрытый интервал
SELECT * FROM orders
WHERE created_at >= '2026-04-01'
AND created_at < '2026-05-01';BETWEEN даёт >= AND <=, но для времени лучше < following_day.
9. NOT IN с NULL
-- если есть хоть один NULL в подзапросе — результат пустой
SELECT * FROM users
WHERE user_id NOT IN (SELECT user_id FROM orders);
-- правильно: отфильтровать NULL
SELECT * FROM users
WHERE user_id NOT IN (
SELECT user_id FROM orders WHERE user_id IS NOT NULL
);
-- или NOT EXISTS (безопасно)
SELECT u.* FROM users u
WHERE NOT EXISTS (
SELECT 1 FROM orders o WHERE o.user_id = u.user_id
);Почему? x NOT IN (a, b, NULL) = x != a AND x != b AND x != NULL = x != a AND x != b AND NULL = NULL (не TRUE).
10. HAVING после WHERE
-- WHERE убрал все активные, HAVING ничего не найдёт
SELECT user_id, COUNT(*)
FROM orders
WHERE status = 'cancelled'
GROUP BY user_id
HAVING COUNT(*) > 100;Уточните логику: WHERE — до агрегации, HAVING — после.
11. AND вместо OR в WHERE
-- ни один пользователь не из двух городов сразу
SELECT * FROM users
WHERE city = 'Moscow' AND city = 'SPb';
-- правильно
SELECT * FROM users
WHERE city = 'Moscow' OR city = 'SPb';
-- или
SELECT * FROM users WHERE city IN ('Moscow', 'SPb');12. Join на неправильном ключе
-- orders.customer_id vs users.id
SELECT * FROM users u
JOIN orders o ON o.user_id = u.id;
-- может быть, в orders это 'customer_id'?Проверьте имена колонок.
13. Тип данных несовпадает
-- user_id — строка, а сравниваете с числом
WHERE user_id = 42 -- если user_id VARCHAR → ok, если INT → ok
WHERE user_id = '42' -- подстраховывает CAST, но может не использовать индексКак дебажить
Пошагово убирать фильтры
-- исходный запрос возвращает пусто
SELECT * FROM users WHERE city = 'Moscow' AND age > 30 AND is_premium;
-- уберём один
SELECT * FROM users WHERE city = 'Moscow' AND age > 30; -- ещё пусто?
-- убираем ещё
SELECT * FROM users WHERE city = 'Moscow'; -- есть результаты?
-- найдите, какой фильтр лишает данныхПроверить distinct values
-- какие city на самом деле в таблице
SELECT DISTINCT city FROM users ORDER BY 1;Посмотреть пример строк
SELECT * FROM users LIMIT 5;COUNT до и после фильтра
SELECT COUNT(*) FROM users; -- всего
SELECT COUNT(*) FROM users WHERE city = 'Moscow'; -- после фильтраНа собесе
Вопрос: «Запрос возвращает пусто, что делать?»
Хороший ответ:
- Проверить опечатки и регистр значений
- Проверить NULL в сравнениях
- Проверить INNER vs LEFT JOIN
- Проверить границы дат / таймзоны
- Пошагово убирать условия
Связанные темы
FAQ
Почему col = NULL не работает?
В SQL NULL = NULL это UNKNOWN. Используйте IS NULL.
Почему LEFT JOIN возвращает только совпадения?
Потому что в WHERE есть условие на правую таблицу. Перенесите в ON.
NOT IN или NOT EXISTS?
NOT EXISTS безопаснее с NULL.
BETWEEN или >= ... <?
Для времени — >= ... <. BETWEEN теряет последний день с временем.
Тренируйте SQL — откройте тренажёр с 1500+ вопросами для собесов.