CHECK пропустил неизвестную цену: почему нужен NOT NULL
В таблице прайс-листа стоит CHECK (price > 0). Отрицательная цена действительно отклоняется, но после очередного импорта нашлась запись без цены. Ограничение выглядит достаточно строгим, поэтому хочется искать обход проверки в загрузчике.
В SQL сравнение NULL с числом даёт неизвестный результат. CHECK отклоняет строку, когда выражение ложно; неизвестное значение не является нарушением этого ограничения. Условие положительности и обязательность значения нужно выразить отдельно.
Как изменить решение
CREATE TABLE offer (
id bigint PRIMARY KEY,
price numeric(12, 2) NOT NULL CHECK (price > 0)
);
INSERT INTO offer VALUES (1, 100.00);
-- Оба следующих INSERT должны завершиться ошибкой:
INSERT INTO offer VALUES (2, NULL);
INSERT INTO offer VALUES (3, -1.00);Положительная цена сохраняется, NULL нарушает NOT NULL, отрицательное число нарушает CHECK. Сообщения различаются, что помогает правильно объяснять причину отказа в импорте и не записывать все ошибки в одну категорию.
Если цена действительно может быть неизвестна, оставьте NULL и явно определите поведение отчётов: исключать такую строку из расчёта маржи или показывать отдельный счётчик. Подстановка нуля меняет смысл: товар с неизвестной ценой начинает выглядеть бесплатным.
Что проверить на своих данных
Для существующей таблицы сначала посчитайте WHERE price IS NULL и WHERE price <= 0. Не исправляйте оба набора одним UPDATE без решения владельца данных. В тестах нужны положительная, нулевая, отрицательная и отсутствующая цена; проверка только отрицательного значения не ловит исходную проблему.
Та же логика важна для составных правил. CHECK (end_at > start_at) не требует наличия обеих дат. Если окончание может отсутствовать у открытого периода, это нормально, но start_at тогда стоит сделать обязательным. Ограничение должно описывать допустимые состояния предметной области, а не только удобное сравнение двух столбцов.
Практика дальше
Следующая практика — запросы, где правильность зависит от нескольких источников и граничных случаев. В курсе «Продвинутый SQL» вы решаете 109 задач на JOIN, окна и CTE, сверяете отчёты и собираете витрину интернет-магазина. Запросы выполняются в браузере на SQLite с автоматической проверкой.
Посмотреть программу и практику курса
Источник: CHECK пропустил неизвестную цену — официальная документация.
Другие задачи по теме: PostgreSQL: запись, блокировки и планы запросов.
Продолжить на практике
- Продвинутый SQL: сложные запросы и задачи для собеседований ↗
Продолжение базового SQL: сложные JOIN, оконные рамки, временные ряды, retention, воронки и рекурсивные CTE. Разбирайте примеры и пишите запросы с автоматической проверкой — от отдельных расчётов до витрины интернет-магазина.
