CHECK-ограничения, соответствующие бизнес-правилу
Обрабатывайте NULL явно, задавайте допустимые состояния строки и учитывайте границы CHECK-ограничений при защите целостности SQL Server.
Ограничение с именем PositiveQuantity словно обещает положительное количество в каждой строке. Если выражение содержит Quantity > 0, а столбец допускает NULL, обещание неполно. SQL Server отклоняет FALSE, но UNKNOWN из-за NULL может пройти. Название является документацией и не добавляет проверок.
Явно описать допустимые состояния
Первая таблица показывает пробел: вставка NULL успешна. Если количество обязательно, сочетайте NOT NULL с положительным диапазоном. Если неизвестное количество допустимо, документируйте этот смысл вместо утверждения, что CHECK требует число.
Вторая таблица моделирует публикацию. Черновик не должен иметь дату публикации, опубликованная строка обязана ее иметь. Status обязателен и ограничен двумя значениями.
CREATE TABLE #WeakRule
(
ItemId int PRIMARY KEY,
Quantity int NULL CHECK (Quantity > 0)
);
INSERT #WeakRule VALUES (1, NULL);
SELECT ItemId, Quantity FROM #WeakRule;
CREATE TABLE #PublicationRule
(
ItemId int PRIMARY KEY,
Status varchar(10) NOT NULL
CHECK (Status IN ('Draft', 'Published')),
PublishedAt datetime2(0) NULL,
CHECK
(
(Status = 'Draft' AND PublishedAt IS NULL)
OR
(Status = 'Published' AND PublishedAt IS NOT NULL)
)
);
INSERT #PublicationRule VALUES
(1, 'Draft', NULL),
(2, 'Published', '20250115T12:00:00');
BEGIN TRY
INSERT #PublicationRule VALUES (3, 'Published', NULL);
END TRY
BEGIN CATCH
SELECT ERROR_NUMBER() AS ConstraintError;
END CATCH;
SELECT * FROM #PublicationRule ORDER BY ItemId;
DROP TABLE #PublicationRule;
DROP TABLE #WeakRule;
Слабое правило возвращает строку NULL. Таблица публикации принимает первые две строки и отклоняет третью. Явные IS NULL и IS NOT NULL исключают случайное UNKNOWN в связи между столбцами.
До реализации составного условия напишите небольшую таблицу истинности. Проверьте Draft с датой и без нее, Published с датой и без нее, неизвестный статус и NULL-статус. Эти случаи превращаются в проверки миграции и интеграции. Одни успешные вставки не проверяют главную защиту.
Сохраняйте читаемость выражений. Длинная цепочка отрицаний может быть правильной, но опасной при расширении. Разделяйте независимые диапазоны значений и отношения между столбцами. Понятные имена постоянных ограничений помогают разбирать ошибки в эксплуатации.
Понимать границу проверки строки
CHECK подходит для инвариантов строки: допустимого диапазона или конца позже начала. Это не планировщик, который пересматривает существующие строки с течением времени. Условие с текущими часами проверяется при соответствующей записи, а не непрерывно после наступления полуночи.
Не скрывайте межстрочные гарантии остатков или минимального количества записей в скалярной функции, вызываемой CHECK. Другие строки могут измениться без повторной проверки исходной строки. Удаления также создают пробелы. Используйте подходящие реляционные ограничения либо транзакцию, действительно защищающую общий инвариант.
Внешний ключ выражает принадлежность другой таблице яснее пользовательской функции поиска. Уникальное ограничение надежнее подсчета видимых дубликатов. Правило 'только одно текущее назначение' может требовать уникального фильтрованного индекса, а не проверки дат каждой отдельной записи.
Ограничения не заменяют авторизацию. Структурно правильная строка может принадлежать арендатору, которого вызывающая сторона не вправе менять. Проверка прав и проверка формы данных остаются разными частями контракта.
Внедрить правило без сокрытия старых нарушений
При добавлении проверяемого ограничения существующие данные должны ему соответствовать. Найдите нарушения и определите исправление или карантин. Заполнение отсутствующих значений выдуманными умолчаниями ради успешной миграции меняет бизнес-содержание.
Ограничение может быть включено, но не доверено, если старые данные не проверялись. Смотрите is_disabled и is_not_trusted в sys.check_constraints. Для существующего ограничения WITH CHECK CHECK CONSTRAINT проверяет строки и устанавливает доверие. На большой таблице запланируйте работу и блокировки.
Проверяйте UPDATE наряду с INSERT. Переход Draft в Published должен задавать PublishedAt той же инструкцией. Изменение сначала статуса, затем даты создает запрещенное промежуточное состояние. Такой отказ полезен: он требует согласованного перехода.
Определите понятные сообщения приложения для ошибок ограничений. Ранняя валидация улучшает интерфейс, база остается последней защитой. При добавлении Archived меняйте модель состояний, миграцию и тесты вместе. Полезно также просмотреть фоновые загрузки и административные скрипты: они могут выполнять переходы иначе, чем основной API. Новая согласованная схема должна поддерживаться всеми путями записи, а не только экраном, ради которого ее придумали. Ослабление CHECK до исчезновения ошибки не заменяет эту проверку.
Техническая документация: Microsoft Learn: CHECK constraints · Microsoft Learn: sys.check_constraints.