Способно ли ограничение CHECK гарантировать отсутствие одинаковых значений в разных строках?
Нет. CHECK проверяет условие для каждой изменяемой строки отдельно и не предназначен для сравнения строки с другими строками таблицы. Для запрета повторов используют UNIQUE или PRIMARY KEY, а для более сложных межстрочных правил — другие механизмы целостности.
Ограничения SQL разделяют проверки локального значения и правила, связывающие несколько объектов. CHECK решает задачу формулировки допустимого состояния одной строки: например, сумма неотрицательна или дата окончания не раньше даты начала.
Для часто встречающихся межстрочных требований существуют специализированные ограничения. Так, уникальность вынесена в UNIQUE, ссылочная целостность — во внешний ключ, поскольку их можно проверять и поддерживать эффективнее, чем произвольное условие внутри CHECK.
Попытка выразить запрет дубликатов через CHECK концептуально неверна: проверяемое условие не является проверкой всего множества строк. Даже если условие выглядит логично для одной строки, оно не гарантирует, что другая строка с тем же значением не будет вставлена позже.
Особенно опасна самодельная проверка через запрос к той же таблице. Она может зависеть от изоляции транзакций и не защитить от двух параллельных вставок, каждая из которых по отдельности не видит незакоммиченные данные другой транзакции.
CHECK вычисляется в контексте проверяемой строки. Если результат условия равен FALSE, изменение отклоняется; результат TRUE обычно принимается. Значение UNKNOWN, возникающее, например, из-за NULL, также не считается нарушением CHECK, поэтому для обязательности значения требуется отдельное NOT NULL.
Запрет одинаковых значений выражают ограничением UNIQUE. СУБД обычно обеспечивает его через индекс или эквивалентный внутренний механизм, который проверяет конфликтующие ключи с учётом конкурентных операций и не допускает успешной фиксации двух нарушающих строк.
В примере CHECK проверяет содержимое каждой строки, а UNIQUE запрещает повторение значения login между строками. Эти ограничения дополняют друг друга и не заменяют одно другое.
Если правило зависит от нескольких строк, выбор механизма определяется его формой. Для ссылок используется FOREIGN KEY, для уникальной комбинации — составное UNIQUE, а сложные правила могут потребовать триггера, блокировок или подходящего специализированного ограничения конкретной СУБД.
В системе бронирования требовалось не допускать двух активных бронирований одного ресурса на один временной интервал. Вариант с CHECK был отклонён: он проверяет отдельное бронирование и не обнаруживает пересечение с уже существующей строкой.
Простое UNIQUE по идентификатору ресурса тоже не подошло бы, поскольку оно запретило бы любые два бронирования ресурса, даже если интервалы не пересекаются. Триггер мог бы сравнивать интервалы, но потребовал бы аккуратной настройки блокировок и анализа конкурентных транзакций.
Выбрали механизм, поддерживающий проверку пересечения диапазонов в используемой СУБД, и дополнили его ограничениями на обязательные поля. Это решение точнее выразило бизнес-правило и обеспечило проверку на уровне базы, а не только в приложении.
1. Достаточно ли сделать SELECT-проверку перед INSERT, чтобы обеспечить уникальность?
Нет. Две параллельные транзакции могут одновременно не найти совпадений, после чего обе вставят одинаковое значение. Гарантию должен обеспечивать механизм базы данных, например UNIQUE, а приложение обязано корректно обрабатывать ошибку нарушения ограничения.
2. Может ли составное ограничение UNIQUE заменить CHECK для правила над несколькими столбцами одной строки?
Нет, это разные задачи. Составное UNIQUE запрещает повторение комбинации значений между строками, но не проверяет произвольное соотношение столбцов внутри одной строки. Для условия вроде "нижняя граница не больше верхней" нужен CHECK.
3. Всегда ли CHECK не может обращаться к другим строкам?
На переносимый SQL-ответ рассчитывать нельзя: CHECK предназначен для локальной проверки строки, а возможность подзапросов и их поведение зависят от диалекта и ограничений конкретной СУБД. Межстрочное правило следует выражать специализированным ограничением либо явно реализовывать механизмом, который учитывает конкурентный доступ; проверка в прикладном коде сама по себе недостаточна.