В бронировании дата окончания не должна быть раньше даты начала. Каким ограничением схемы выразить это прав...

В бронировании дата окончания не должна быть раньше даты начала. Каким ограничением схемы выразить это правило?

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

Для этого используют ограничение CHECK, сравнивающее дату окончания с датой начала. Если обе даты обязательны, их дополнительно объявляют NOT NULL, чтобы неизвестное значение не позволяло обойти проверку.

Исторический контекст

Декларативные ограничения появились как способ переносить правила целостности из прикладного кода в саму базу данных. Это защищает данные независимо от того, какое приложение, пользователь или интеграция выполняет запись.

Проверка диапазона дат — типичное правило, относящееся к одной строке. Его удобно описывать декларативно, поскольку базе не требуется сопоставлять строку с другими строками или таблицами.

Постановка проблемы

Если правило проверяется только в приложении, разные пути записи могут вести себя по-разному: один сервис проверит даты, другой забудет это сделать, а прямой импорт вообще обойдёт проверку. В результате в таблице появятся бронирования с отрицательной длительностью.

Нужно явно определить, разрешён ли интервал нулевой длины. Условие окончания не раньше начала допускает равные даты, поэтому для запрета такого случая требуется более строгое сравнение.

Подробное решение

Ограничение CHECK проверяет логическое выражение для каждой вставляемой или изменяемой строки. Если выражение ложно, операция отклоняется; если истинно — строка проходит эту проверку.

CREATE TABLE booking ( booking_id INTEGER PRIMARY KEY, starts_on DATE NOT NULL, ends_on DATE NOT NULL, CHECK (ends_on >= starts_on) );

В примере база принимает только строки, где ends_on не меньше starts_on. Ограничение действует при INSERT и при UPDATE, поэтому нельзя нарушить правило последующим изменением одной из дат.

CHECK контролирует только значения текущей строки. Он не запрещает пересечение двух бронирований одного помещения, не проверяет рабочий календарь и не гарантирует, что дата находится в будущем. Такие правила требуют других механизмов: внешних ключей, уникальных или специализированных ограничений, триггеров либо транзакционной логики.

Если даты допускают NULL, результат проверки может быть не истинным и не ложным, а UNKNOWN; в стандартной трехзначной логике это обычно не считается нарушением CHECK. Поэтому для обязательного диапазона нужны NOT NULL на обеих датах. Если отсутствие одной из дат является допустимым состоянием, его следует моделировать осознанно, а не рассчитывать на поведение CHECK.

Ситуация из практики

В системе аренды автомобиля разработчик сначала проверял порядок дат только в веб-интерфейсе. После появления пакетного импорта выяснилось, что часть записей содержит дату возврата раньше даты выдачи.

Рассматривались два варианта. Оставить проверку в приложении было проще для быстрого исправления, но это не защищало бы базу от других клиентов. Добавить триггер можно, однако он сложнее для сопровождения и не нужен для простого правила одной строки.

Выбрали CHECK (ends_on >= starts_on) и NOT NULL для обеих дат. Это централизовало базовое правило, сохранило одинаковое поведение для всех каналов записи и не создало лишней процедурной логики. Отдельно добавили проверку пересечений интервалов, поскольку обычный CHECK для неё недостаточен.

Что кандидаты часто упускают

  1. Можно ли с помощью этого CHECK запретить пересечение бронирований?

    Нет. CHECK оценивает одну строку и не предназначен для поиска конфликтующих строк. Для запрета пересечений требуется ограничение, поддерживающее сравнение интервалов, либо триггер или корректно изолированная транзакционная логика — выбор зависит от СУБД и требований к конкуренции.

  2. Что произойдёт, если даты начала и окончания равны?

    При условии ends_on >= starts_on такая строка будет разрешена, потому что нулевой интервал удовлетворяет нестрогому сравнению. Если бронирование должно иметь положительную длительность, используют условие ends_on > starts_on.

  3. Достаточно ли изменить CHECK, если дата окончания может отсутствовать до завершения бронирования?

    Нет, нужно определить модель состояния. Если незавершённое бронирование действительно может не иметь даты окончания, ends_on оставляют nullable и отдельно задают правила для такого состояния, например связывают допустимость NULL со статусом. Если дата окончания обязательна после определённого этапа жизненного цикла, одной проверки диапазона недостаточно: потребуется дополнительное ограничение, триггер или транзакционная бизнес-логика.