В таблице нужно запретить повторение пары значений: почему ограничение уникальности должно описывать комбинацию столбцов, а не каждый столбец отдельно?
Ограничение уникальности должно применяться к комбинации столбцов, если уникальна именно пара значений. Отдельные ограничения на каждый столбец требуют, чтобы каждое значение встречалось только один раз, поэтому они запрещают допустимые сочетания.
В реляционной модели строка может идентифицироваться не одним атрибутом, а составным ключом — набором столбцов. Например, один и тот же товар может встречаться у разных поставщиков, а один поставщик может продавать разные товары; уникальной является именно пара «товар — поставщик».
SQL поддерживает такую модель через составные ограничения UNIQUE и составные первичные ключи. Это позволяет выражать правило целостности на уровне схемы, а не контролировать его только в коде приложения.
Предположим, требуется разрешить строки (товар 1, поставщик 1) и (товар 1, поставщик 2), но запретить повторное появление (товар 1, поставщик 1). Если сделать каждый столбец отдельно уникальным, вторая строка с тем же товаром будет ошибочно отклонена.
Если вообще не задать ограничение на пару, база данных допустит дубликаты, а результат будет зависеть от дисциплины приложения, конкурентных транзакций и качества проверок.
Составное ограничение UNIQUE проверяет уникальность всей комбинации значений. Для двух столбцов (a, b) нарушением считается наличие другой строки с той же парой, а не совпадение только a или только b.
В этом примере допустимы разные поставщики для одного товара и разные товары у одного поставщика. Повторная вставка той же пары будет отклонена.
Ограничения UNIQUE (product_id) и UNIQUE (supplier_id) имели бы другое значение: каждое значение стало бы глобально уникальным в своей колонке. Это существенно более сильное правило и обычно не соответствует предметной модели.
Для составного PRIMARY KEY действует тот же принцип уникальности комбинации, но дополнительно все его столбцы становятся обязательными. У UNIQUE обработка NULL зависит от правил SQL и конкретной СУБД, поэтому при необходимости полной идентификации пары столбцы обычно явно объявляют NOT NULL.
В интернет-магазине нужно хранить цену товара у каждого поставщика. Рассматривались три варианта.
Отдельная уникальность товара не подходит: она позволит указать цену только одного поставщика. Отдельная уникальность поставщика также неверна: поставщик не сможет продавать несколько товаров. Отсутствие ограничения допускает дубли и может привести к нескольким противоречивым ценам для одной пары.
Выбран составной UNIQUE по идентификаторам товара и поставщика, а оба столбца сделаны обязательными. В результате база данных сама защищает бизнес-правило, а конкурентные операции не могут незаметно создать дубликат.
Чем составной UNIQUE отличается от составного PRIMARY KEY?
PRIMARY KEY задаёт основной идентификатор строки: в таблице он может быть только один, и его значения не могут быть NULL. Ограничений UNIQUE может быть несколько; они задают дополнительные альтернативные правила уникальности. UNIQUE не всегда делает столбцы обязательными, поэтому для этого часто требуется явный NOT NULL.
Создаёт ли составное UNIQUE индекс?
Во многих СУБД для проверки ограничения создаётся или используется уникальный индекс, но стандарт SQL описывает прежде всего ограничение, а не конкретный способ физической реализации. Такой индекс обычно ускоряет поиск по ведущим столбцам в заданном порядке, однако полагаться на одинаковое устройство индексов во всех СУБД нельзя.
Почему проверку уникальности нельзя надёжно заменить предварительным SELECT в приложении?
Между проверкой и вставкой другая транзакция может добавить ту же пару значений. Это классическая гонка: два процесса одновременно увидят отсутствие строки и оба попытаются вставить дубликат. Ограничение UNIQUE проверяется самой СУБД с учётом конкурентного доступа, поэтому именно оно должно быть окончательным гарантом целостности; предварительный запрос может использоваться только для улучшения сообщения об ошибке.