CHECK и NOT NULL: два разных обещания таблицы
Почему CHECK (price > 0) допускает NULL и как вместе с NOT NULL задать обязательную положительную цену.
- NOT NULLЗапрещает отсутствие значения
- CHECK (price > 0)Отклоняет ноль и отрицательные числа
- Оба ограниченияЦена заполнена и больше нуля
Цена должна быть известна и больше нуля. Добавляем CHECK (price > 0), но строка с NULL всё равно сохраняется. Это не ошибка PostgreSQL: сравнение с NULL не возвращает ложь. Для обязательного значения нужно второе ограничение — NOT NULL. Проверим оба правила на небольшой таблице.
Два независимых ограничения
CREATE TABLE demo_prices (
sku text PRIMARY KEY,
price numeric(12,2) NOT NULL,
CONSTRAINT positive_price CHECK (price > 0)
);
INSERT INTO demo_prices VALUES ('book', 120.00);
SELECT sku, price FROM demo_prices;CHECK отклоняет строку, когда его выражение возвращает ложь. Неизвестный результат, возникающий при сравнении NULL с числом, сам по себе не является нарушением этой проверки. Поэтому NOT NULL нужен отдельно. Именованное ограничение диапазона помогает связать сообщение сервера с конкретным правилом, не разбирая текст запроса и не угадывая назначение столбца.
Проверяем отрицательные случаи
-- Выполняйте каждую команду отдельно, вне явной транзакции.
INSERT INTO demo_prices VALUES ('bad-zero', 0);
INSERT INTO demo_prices VALUES ('bad-null', NULL);
SELECT count(*) FROM demo_prices;Обе вставки должны завершиться ошибкой, а количество сохранённых строк должно остаться равным единице. Если объединить проверки в транзакцию, первая ошибка переведёт её в прерванное состояние; для продолжения понадобится откат или заранее подготовленная точка сохранения.
Для каких правил подходит CHECK
CHECK удобно выражает зависимость между полями одной строки: например, окончание интервала должно быть позже начала. Не используйте такое ограничение как самодельную проверку суммы по соседним строкам или наличия записи в другой таблице. Для ссылок предусмотрен внешний ключ, а межстрочные бизнес правила требуют отдельного проектирования конкурентного доступа.
Перед добавлением ограничений найдите старые нулевые цены и пропуски. Их исправление согласуйте с теми, кто отвечает за данные: заменить неизвестную цену нулём — значит изменить смысл записи. После миграции проверьте определение таблицы, правильную вставку и оба отказа. Правило должно работать и при записи в обход формы.
