Практика: типичные ошибки в условиях
Практика: типичные ошибки в условиях
Этот урок завершает модуль про фильтрацию и сортировку. Вы уже освоили WHERE, логические операторы AND/OR/NOT, ORDER BY, LIMIT/OFFSET и приоритет операторов. Теперь разберём самые частые ошибки, которые делают новички при написании условий WHERE. Умение находить и исправлять такие ошибки — важный навык, который отличает уверенного SQL-разработчика от начинающего.
Ошибка 1: сравнение с NULL через «=»
Пожалуй, самая распространённая ловушка в SQL. Попытка найти строки, где колонка пустая:
-- Неправильно: никогда не вернёт строки
SELECT * FROM customers WHERE phone = NULL;
-- Правильно
SELECT * FROM customers WHERE phone IS NULL;
Почему = NULL не работает? В SQL NULL означает «неизвестное значение». Сравнение «неизвестное = неизвестное» даёт не TRUE и не FALSE, а опять NULL. А строки, где условие WHERE даёт NULL, фильтруются — они не попадают в результат.
Единственные операторы для проверки на NULL: IS NULL и IS NOT NULL.
-- Правильно: найти клиентов без email
SELECT name FROM customers WHERE email IS NULL;
-- Правильно: найти клиентов с email
SELECT name FROM customers WHERE email IS NOT NULL;
Проверь себя: что вернёт WHERE column = NULL для строки, где column IS NULL?
Ошибка 2: регистр строк
PostgreSQL чувствителен к регистру строковых значений. Если данные записаны как 'Москва', а вы ищете 'москва' — результата не будет:
-- Данные в таблице: 'Москва'
SELECT * FROM customers WHERE city = 'москва'; -- 0 строк
SELECT * FROM customers WHERE city = 'МОСКВА'; -- 0 строк
SELECT * FROM customers WHERE city = 'Москва'; -- нашли!
Если регистр данных в базе непредсказуем (кто-то написал «москва», кто-то «Москва», кто-то «МОСКВА»), используйте функцию LOWER() для приведения к единому регистру перед сравнением:
SELECT * FROM customers WHERE LOWER(city) = 'москва';
Это вернёт строки независимо от того, как записан город. Обратная сторона: LOWER(city) вычисляется для каждой строки, что может быть медленнее. На больших таблицах для таких запросов создают специальные индексы.
Ошибка 3: одинарные и двойные кавычки
В PostgreSQL одинарные и двойные кавычки — разные вещи:
-- Правильно: строковый литерал в одинарных кавычках
SELECT * FROM products WHERE name = 'Ноутбук';
-- Ошибка в PostgreSQL: двойные кавычки — для имён объектов
SELECT * FROM products WHERE name = "Ноутбук";
-- ERROR: column "Ноутбук" does not exist
PostgreSQL интерпретирует двойные кавычки как имя колонки, а не как строку. Результат: «колонки с таким именем не существует».
Строковые значения — только в одинарных кавычках.
Ошибка 4: использование псевдонима в WHERE
Напомним ошибку из урока про WHERE:
-- Неправильно
SELECT price * 1.2 AS price_vat
FROM products
WHERE price_vat > 5000; -- ERROR: column "price_vat" does not exist
-- Правильно
SELECT price * 1.2 AS price_vat
FROM products
WHERE price * 1.2 > 5000;
WHERE выполняется до SELECT, поэтому псевдонимы из SELECT-списка ещё не существуют в момент применения WHERE. Нужно повторить выражение.
Ошибка 5: потеря условия из-за приоритета AND/OR
Классическая ошибка приоритета, которую мы подробно разбирали:
-- Запрос с ошибкой: ищем "активных из Москвы или активных из Питера"
-- Но получаем "всех из Москвы + активных из Питера"
SELECT * FROM customers
WHERE city = 'Москва' OR city = 'Санкт-Петербург' AND status = 'active';
-- Правильный запрос
SELECT * FROM customers
WHERE (city = 'Москва' OR city = 'Санкт-Петербург') AND status = 'active';
Признак этой ошибки: запрос возвращает «лишние» строки, которые не должны были попасть в результат.
Ошибка 6: лишний DISTINCT там, где он не нужен
Иногда DISTINCT добавляют «на всякий случай», хотя дубликатов нет:
-- Если id — первичный ключ, DISTINCT ничего не делает, но тормозит
SELECT DISTINCT id, name FROM products ORDER BY id;
-- Правильно: просто без DISTINCT
SELECT id, name FROM products ORDER BY id;
DISTINCT вынуждает СУБД сравнивать все строки между собой. Если уникальность и так гарантирована (например, запрос по первичному ключу), DISTINCT — лишняя нагрузка.
Ошибка 7: LIMIT без ORDER BY
Получить «первые 10 товаров» без сортировки:
-- Результат непредсказуем
SELECT name, price FROM products LIMIT 10;
-- Предсказуемый результат: 10 дешевейших товаров
SELECT name, price FROM products ORDER BY price ASC LIMIT 10;
Без ORDER BY СУБД возвращает строки в произвольном порядке — каком именно, зависит от внутреннего хранения данных и может меняться от запуска к запуску.
Ошибка 8: неверный знак сравнения
Путаница между «включительно» и «исключительно»:
-- Хотели пользователей от 18 лет включительно
-- Написали строго больше — потеряли 18-летних
SELECT * FROM users WHERE age > 18; -- 19, 20, 21...
-- Правильно: от 18 и старше
SELECT * FROM users WHERE age >= 18; -- 18, 19, 20...
Аналогично с верхней границей: < 30 исключает 30-летних, <= 30 включает.
Особенно часто ошибка встречается при работе с датами:
-- Хотели все заказы за 2024 год
-- Написали < '2025-01-01' — правильно, но можно написать понятнее
SELECT * FROM orders WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01';
-- Альтернатива: EXTRACT или date_trunc (изучим позже)
Ошибка 9: забытые строки из-за NULL в логических операциях
Тонкая ошибка: колонка может содержать NULL, и это «отравляет» результат логических операций.
-- Хотим всех сотрудников с зарплатой не равной 100000
SELECT name FROM employees WHERE salary <> 100000;
Казалось бы, всё просто. Но строки, где salary IS NULL, в результат не попадут — NULL <> 100000 даёт NULL, а не TRUE.
Если нужно включить и строки с NULL:
SELECT name FROM employees WHERE salary <> 100000 OR salary IS NULL;
Ошибка 10: опечатка в имени таблицы или колонки
Банальная, но частая ошибка:
SELECT emplyee_name FROM employes;
-- ERROR: relation "employes" does not exist
PostgreSQL не делает «умных» предположений. «Нет такой таблицы» — значит нет. Проверьте написание через \dt (в psql) или воспользуйтесь автодополнением в GUI-клиенте.
Подход к отладке: делите и властвуйте
Когда запрос возвращает не то, что ожидалось, полезна стратегия «разбить на части»:
- Запустите запрос без
WHERE— убедитесь, что данные в таблице вообще есть - Добавляйте условия
WHEREпо одному — находите, при каком условии «теряются» нужные строки - Проверьте значения в базе:
SELECT DISTINCT city FROM customersпокажет реальный список городов - Проверьте
NULL:SELECT * FROM customers WHERE phone IS NULL— есть ли пустые телефоны?
Такой подход позволяет быстро найти ошибку даже в сложном условии из 5–6 частей.
Ещё один приём — вынести сложное условие в простые части и проверить каждую:
-- Оригинальный запрос с подозрением на ошибку
SELECT * FROM orders
WHERE status = 'new' OR status = 'pending' AND total > 1000;
-- Шаг 1: проверить сколько строк при только первом условии
SELECT count(*) FROM orders WHERE status = 'new';
-- Шаг 2: проверить сколько строк при только втором условии
SELECT count(*) FROM orders WHERE status = 'pending' AND total > 1000;
-- Шаг 3: сравнить сумму с результатом оригинала — если не совпадает, значит логика неверна
SELECT count(*) FROM orders WHERE status = 'new' OR status = 'pending' AND total > 1000;
Если сумма count из шагов 1 и 2 не совпадает с итогом из шага 3 — в условии есть пересечение или ошибка приоритета.
Краткий итог
= NULLне работает — используйтеIS NULLиIS NOT NULL- PostgreSQL чувствителен к регистру строк;
LOWER()помогает при непредсказуемых данных - Строки — в одинарных кавычках; двойные — для имён объектов
- Псевдонимы
SELECTнедоступны вWHERE— повторяйте выражение - Приоритет
AND > OR— при смешивании всегда используйте скобки NULLв<>условии не возвращаетTRUE— добавляйте явную проверкуOR IS NULL- При отладке: убирайте условия по одному, проверяйте реальные значения в базе
Что дальше
Вы завершили модуль 3. Теперь вы умеете фильтровать, сортировать, ограничивать результат и правильно выстраивать логику условий. В следующем модуле — специализированные операторы: LIKE для поиска по шаблону, IN для списков значений, BETWEEN для диапазонов, IS NULL, и мощный CASE WHEN для условных выражений.