Unique Constraints SQL Server
В SQL Server (Transact-SQL) Unique Constraints как создавать, удалять, отключать, и включать unique constraints (уникальные ограничения)
Уникальное ограничение — это одно поле или комбинация полей, которые однозначно определяют запись. Некоторые из полей могут содержать нулевые значения, если комбинация значений уникальна.
В чем разница между уникальным ограничением и первичным ключом?
Первичный ключ — поля, которые являются частью первичного ключа, не могут содержать значение NULL.
Уникальное ограничение — некоторые поля, которые являются частью уникального ограничения, могут содержать значения NULL, если комбинация значений уникальна.
Создание уникального ограничения — использование оператора CREATE TABLE
Синтаксис создания уникального ограничения с использованием оператора CREATE TABLE в SQL
CREATE TABLE table_name
(
column1 datatype [ NULL | NOT NULL ],
column2 datatype [ NULL | NOT NULL ],
.
CONSTRAINT constraint_name UNIQUE (uc_col1, uc_col2, . uc_col_n)
);
table_name — имя таблицы, которую вы хотите создать.
column1 , column2 — столбцы, которые вы хотите создать в таблице.
constraint_name — имя уникального ограничения.
uc_col1 , uc_col2 , . uc_col_n — столбцы, которые составляют уникальное ограничение.
Пример
Рассмотрим пример создания уникального ограничения в SQL Server с помощью опертора CREATE TABLE.
SQL UNIQUE
Ограничение UNIQUE в SQL позволяет идентифицировать каждую запись в таблице. Если помещается ограничение столбца UNIQUE в поле при создании таблицы, база данных отклонит любую попытку ввода в это поле для одной из строк, значения, которое уже представлено в другой строке. Это ограничение может применяться только к полям, которые были объявлены как непустые (NOT NULL), так как не имеет смысла позволить одной строке таблицы иметь значение NULL, а затем исключать другие строки с NULL значениями как дубликаты.
SQL Server / Oracle / Access
Пример создания таблицы SQL с ограничением UNIQUE:
CREATE TABLE Student ( Kod_stud integer NOT NULL UNIQUE, Fam char (30) NOT NULL UNIQUE, Adres char (50), Ball decimal);
Когда обьявляется поле Fam уникальным, две Смирновых Марии могут быть введены различными способами — например, Смирнова Мария и Смирнова М. Столбцы (не первичные ключи), чьи значения требуют уникальности, называются ключами-кандидатами или уникальными ключами. Можно определить группу полей как уникальную с помощью команды ограничения таблицы — UNIQUE. Объявление группы полей уникальной, отличается от объявления уникальными индивидуальных полей, так как это комбинация значений, а не просто индивидуальное значение, которое обязано быть уникальным. Уникальность группы заключается в том, что пары строк со значениями столбцов «a», «b» и «b», «a» рассматривались отдельно одна от другой.
Если база данных определяет, что каждая специальность принадлежит одному и только одному факультету, то каждая комбинация кода факультета(Kod_f) и кода специальности(Kod_spec) в таблице Spec должна быть уникальной. Например:
CREATE TABLE Spec ( Kod_spec integer NOT NULL, Kod_f integer NOT NULL, Nazv_spec char (50) NOT NULL, UNIQUE (Kod_spec, Kod_f));
Оба поля в ограничении таблицы UNIQUE все еще используют ограничение столбца — NOT NULL. Если бы использовалось ограничение столбца UNIQUE для поля Kod_spec, такое ограничение таблицы было бы необязательным. Если значения поля Kod_spec различно для каждой строки, то не может быть двух строк с идентичной комбинацией значений полей Kod_spec и Kod_f.
Ограничение таблицы UNIQUE наиболее полезно, когда индивидуальные поля не обязательно должны быть уникальными.
MySQL UNIQUE
Пример создания таблицы Persons в MySQL с ограничением UNIQUE:
CREATE TABLE Persons ( P_Id int NOT NULL, LastName varchar(255) NOT NULL, FirstName varchar(255), Address varchar(255), City varchar(255), UNIQUE (P_Id) );
Удалить ограничение UNIQUE
Если после создания ограничения UNIQUE и в том случае, когда ограничение UNIQUE не имеет смысла, UNIQUE можно удалить. Для этого используйте следующий SQL: SQL Server / Oracle / MS Access:
ALTER TABLE table_name DROP CONSTRAINT uc_PersonID;
MySQL:
1.2.4. Ограничения значений полей в SQL
Задача любого администратора и программиста, совместно обеспечить целостность и корректность данных. Например, если пользователь не укажет в одной из строк в списке жителей дома фамилию, то корректность данных портиться. Человека очень сложно найти, если не известна его фамилия.
А что если в нашем списке добавить поле для хранения пола. Что можно записать в такое поле? Конечно же, достаточно одной буквы «М» или «Ж», другого пола пока не придумали :). А что если пользователь случайно введет букву «Ь»? Это не смешно, а вполне реальная ситуация. Посмотрите на клавиатуру и увидите, что на ней две буквы «М», просто одна английская, а другая русская. На одной клавише с английской буквой «М» находиться «Ь». Начинающие пользователи нередко путаются и если не проверят результат своего ввода, то корректность данных нарушается.
Нередко бывают ситуации, когда вместо русской буквы М пользователь вводить английскую. В этом случае в базе получается три пола: М русская, М английская и Ж. Конечно, программно эта ситуация решается достаточно просто, достаточно только при определении мужчин выбирать из базы все записи с буквой М в любой раскладке, но качество такой базы будет далеко от идеала.
Все эти проблемы известны уже давно и для их решения придумали ограничения – универсальное средство, с помощью которого можно задать правила, которым должны удовлетворять данные, для возможности записи в поле. Если записываемое значение не удовлетворяет ограничениям, назначенным полю, то запись завершается ошибкой. Таким образом, сервер сам будет контролировать целостность данных, вводимых пользователем.
Самое простейшее ограничение – это разрешение или запрещение введения нулевых значений (NULL). Тут нужно отметить, что NULL и пустая строка это совершенно разные вещи. Об этом мы еще поговорим во второй главе. Итак, для таблицы, в которой хранятся ФИО, пол и дата рождения не нулевыми должны быть фамилия, имя и пол. Эти параметры есть у каждого жителя нашей страны, поэтому в базе данных обязательно должно быть что-то указано, иначе будет сгенерирована ошибка и сервер не позволит записать такую строку.
А вот с отчеством и датой рождения бывают проблемы, особенно у тех, кто родился во время второй мировой войны. В те времена сложно было определить дату рождения сирот, а в детских домах после войны детям давали только имя и отчество, поэтому отсутствие этих параметров не будет считаться ошибкой.
Следующий пример, показывает, как создаются поля, в которых запрещаются пустые значения:
CREATE TABLE TestTable ( id int DEFAULT 1 NOT NULL, dDate datetime DEFAULT (getdate()) NULL, vcName varchar(50) NOT NULL )
В данном случае указано, что поле id и vcName не могут содержать нулевые значения. Пользователь обязательно должен указать хоть что-нибудь, иначе изменения не будут приняты, и сервер сгенерирует ошибку. А вот поле dDate может содержать нулевое значение NULL.
Что не корректно в этом примере? Хотя нет, некорректного ничего нет, но на первый взгляд есть одна глупость. Для поля dDate мы разрешили нулевое значение, но это бессмысленно. Дело в том, что у этого поля есть значение по умолчанию, а значит, если в поле будет попытка записать нулевое значение, оно будет автоматически заменено на значение по умолчанию. Получается, что указывать NULL бессмысленно, если для поля установлено значение по умолчанию и нулевого значения просто не будет.
Но теперь вспомним, когда генерируется значение по умолчанию – только при вставке строки, а не при обновлении. Вы уловили ход мысли? Если полю назначено значение по умолчанию и при этом запрещено нулевое значение, то ноль нельзя будет установить даже у существующей строки. Именно поэтому я сказал, что глупость есть только на первый взгляд. Никогда не забывайте, когда устанавливается значение по умолчанию.
Ограничения NULL и NOT NULL являются не жесткими и некоторые специалисты даже не относят их к ограничениям, хотя, по своей сути они такими являются. Более жесткие ограничения задаются оператором CHECK. Рассмотрим этот оператор на примере. Допустим, что нам нужно создать список хозяев квартир. Для этого нам понадобиться ключевое поле («id»), имя хозяина («vcName») и номер квартиры («iApartment»). Для квартиры вполне логичным будет сделать ограничение ввода от 1 до 1000. Квартир с отрицательными номерами и нулевыми значениями не бывает (по крайней мере, в моем городе), да и более 1000 у нас не бывает, я даже не видел квартиры, с номером более 400. Поэтому логично будет запретить ввод явно некорректных данных с помощью ограничения. Итак, посмотрим на следующий запрос создания таблицы:
CREATE TABLE TestTable ( id int DEFAULT 1 NOT NULL, vcName varchar(50) NOT NULL, iApartment int CHECK (iApartment>0 and iApartment<1000) )
После указания имени и типа поля "iApartment" указано ключевое слово CHECK, после которого в круглых скобках необходимо указать ограничения для данного поля. В нашем примере в качестве ограничения выступает:
iApartment>0 and iApartmentЭто значит, что значение в поле iApartment должно быть более 0 и в то же время меньше 1000. Объединение происходит с помощью ключевого слова and, то есть «и». Это значит, что оба условия должны быть выполнены. Бывают случаи, когда необходимо, чтобы хотя бы одно из условий было выполнено, тогда их объединяют оператором or. Например, нужно чтобы в поле заносилось значение или меньше нуля, или больше 1000. Значения в промежутке 1 – 1000 указывать нельзя. В этом случае, сравнение выглядело бы:
iApartment1000Попробуйте вставить в таблицу квартир данные. Для этого давайте выполним три запроса. Первый будет вставлять корректные данные:
INSERT INTO TestTable(vcName, iApartment) VALUES('Корректная запись', 10)Не будем вдаваться в подробности работы запроса, это нам предстоит узнать во 2-й главе. Операция пройдет успешно и никаких ошибок не появиться. Следующий запрос будет пытаться вставить запись с номером квартиры 10000, что не соответствует ограничению:
INSERT INTO TestTable(vcName, iApartment) VALUES('Ошибочная запись', 10000)В ответ на это, перед нами должно появиться окно с сообщением об ошибке:
INSERT statement conflicted with COLUMN CHECK constraint 'CK__TestTable__iApar__22AA2996'. The conflict occurred in database 'TestDatabase', table 'TestTable', column 'iApartment'.
The statement has been terminated.
Оператор INSERT конфликтует с COLUMN CHECK ограничением 'CK__TestTable__iApar__22AA2996'. Конфликт обнаружен в базе данных 'TestDatabase', таблица 'TestTable', колонка 'iApartment'.
Выполнение было прервано.
Эта ошибка может быть отображена и в окне результата выполнения запроса, а не в отдельном окне диалога. Смысл сообщения в том, что произошел конфликт с ограничением данных.
Если с помощью Enterprise Manager открыть свойства таблицы TestDatabase, то в появившемся окне на закладке Check Constraints в выпадающем списке Selected constraint выбрать ограничение, которое мы увидели в сообщении (CK__TestTable__iApar__22AA2996), а в окне Constraint expression появится описание ограничения.
Напоследок, выполним запрос, который покажет содержимое таблицы:
SELECT * FROM TestTableВы должны увидеть только одну строку, которую мы создали первой с корректными значениями. Некорректная строка будет отсутствовать в таблице.
Самое неприятное в этом примере – это имя ограничения, которое было назначено автоматически - CK__TestTable__iApar__22AA2996. С такими именами очень неудобно работать, намного приятнее видеть читаемые имена и со смыслом. Чтобы задать имя ограничению, необходимо использовать конструкцию:
CONSTRAINT имя CHECK (ограничения)Имена ограничений внутри базы данных должны быть уникальными. Это значит, что вы не можете создать два ограничения с одним и тем же именем не только для одной и той же таблицы, но и для разных таблиц одной базы данных.
Такие ограничения можно описывать внутри описания поля (как параметры), а можно вынести все в один блок, после описания полей. В этом случае, ограничение должно выглядеть следующим образом:
CONSTRAINT Имя ограничения CHECK (Ограничение)Такое ограничение желательно описывать после всех полей и через запятую, как и поля таблицы. Преимущество такого метода в том, что мы сами задаем имя ограничения, и с ним будет удобнее работать в дальнейшем.
Давайте удалим старую таблицу, а создадим новую с еще одним полем – dDate типа datetime. Для этого поля поставим ограничение, которое позволит вводить только даты, которые меньше текущей:
CREATE TABLE TestTable ( id int DEFAULT 1 NOT NULL, vcName varchar(50) NOT NULL, iApartment int, dDate datetime, CONSTRAINT check_iApartment CHECK (iApartment>0 and iApartment<1000), CONSTRAINT check_dDate CHECK (dDateКак видите, в этом примере ограничения описываются через запятую, так же, как и поля. После ключевого слова CONSTRAINT указывается имя ограничения. Из своей практики хочу порекомендовать называть их в виде check_имя, где имя – имя поля, которое проверяется ограничением. После этого указывается само ограничение точно также как и в предыдущем примере.
Обратите внимание, что для ограничения ввода в поле "dDate" мы использовали функцию (getdate). Как и при описании значений по умолчанию, в ограничениях также могут использоваться функции.
При создании ограничения, можно использовать многие операторы сравнения языка SQL. Например, в SQL есть очень удобный оператор IN. С его помощью можно задать возможные значения для поля, которые оно может принимать. Например, вам нужно в таблице ограничить ввода данных в поле содержащую такую информацию как пол человека. В этом случае, можно разрешить ввод букв "М" и "Ж" следующим образом:
CREATE TABLE TestTable ( id int DEFAULT 1 NOT NULL, vcName varchar(50) NOT NULL, cPol char(1), CONSTRAINT check_cPol CHECK (cPol IN ('М', 'Ж')) )В данном случае, ограничение выглядит следующим образом: cPol IN ('М', 'Ж'). Оператор IN означает, что поле может принимать любые значения, перечисленные в круглых скобках. В нашем случае указано две строки 'М' и 'Ж'. Другие буквы вносить в поле нельзя. Конечно же, то же самое можно было бы написать и следующим образом:
(cPol = 'М' or cPol = 'Ж')Но это не очень удобно, особенно, если очень много возможных вариантов. Ограничение окажется не очень удобным для чтения. Оператор IN в этом случае намного красивее и читабельнее.
Очень мощных возможностей можно добиться, используя в ограничении оператора LIKE. Например, вы хотите, чтобы поле для хранения телефонного номера содержало номер в формате (ХХХ) ХХХ-ХХ-ХХ, где Х – это любая цифра. Для реализации примера такого ограничения создадим новую таблицу с полем "vcPhonenumber":
CREATE TABLE TestTable ( id int DEFAULT 1 NOT NULL, vcPhonenumber varchar(50) NOT NULL CONSTRAINT check_vcPhonenumber CHECK (vcPhonenumber LIKE '([0-9][0-9][0-9]) [0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]') )В данном случае, ограничение с именем check_vcPhonenumber при проверки использует оператор LIKE. С этим оператором мы познакомимся ближе в следующей главе, когда будем изучать работу с оператором SELECT, но я решил вставить этот пример, чтобы вы заранее знали о его мощи. Если классический знак равенства производит жесткое сравнение, то LIKE позволяет сравнивать строки с определенным шаблоном. В данном случае шаблоном является:
В квадратных скобках мы указываем возможный диапазон символа. В данном случае, диапазон от 0 до 9. Если заменить все [0-9] на Х, то мы получим искомый шаблон (ХХХ) ХХХ-ХХ-ХХ. Следующий пример запроса добавляет в таблицу номер телефона (085) 880-08-00:
INSERT INTO TestTable(vcPhonenumber) VALUES('(085) 880-08-00')Операция пройдет успешно. Но если убрать пробел, или любой из символов ( или -, или заменить какую-либо цифру буквой, то операция завершиться неудачей:
INSERT INTO TestTable(vcPhonenumber) VALUES('(095)8800800')В ответ на это мы увидим сообщение:
INSERT statement conflicted with COLUMN CHECK constraint 'check_vcPhonenumber'. The conflict occurred in database 'TestDatabase', table 'TestTable', column 'vcPhonenumber'.
The statement has been terminated.
Смысл сообщения в следующем: «Оператор INSERT конфликтует с ограничением COLUMN CHECK с именем check_vcPhonenumber. Конфликт появился в базе данных TestDatabase, таблице TestTable, колонке vcPhoneNumber. Выполнение оператора прервано».
Следующий пример задает шаблон для ввода даты:
CREATE TABLE TestTable ( id int DEFAULT 1 NOT NULL, vcDate varchar(50) NOT NULL CONSTRAINT check_vcDate CHECK (vcDate LIKE '[0-3][0-9].[0-1][0-9].[0-9][0-9][0-9][0-9]') )Такой шаблон бывает удобным, если дату нужно хранить в текстовом виде. Да, есть специализированный тип datetime, но все же, бывают разные задачи и может пригодиться текстовый хранение. Шаблон, позволяет ограничить действия пользователя, дабы уменьшить вероятность ошибки, но не решает задачу полностью. Давайте посмотрим на него по частям.
Первая часть (до точки) определяет число месяца. Первая цифра числа может принимать значения от 0 до 3, а вторая цифра от 0 до 9. Это значит, что пользователь может ввести числа от 01 до 39. Числа от 32 до 39 заранее являются не корректными. При указании месяца пользователь также может указать неверное значение, потому что это значение мы ограничили значениями от 01 до 19.
Бывает необходимость, чтобы определенное поле или сочетание полей были в базе данных уникальными. Это можно проверять с помощью запросов самостоятельно, а можно доверить проверку серверу. Для создания ограничения уникальности используется ограничение UNIQUE, которое выглядит следующим образом:
CONSTRAINT Имя ограничения UNIQUE (Поле или список полей)Следующий пример создает ограничение уникальности для ключевого поля idName, ведь не даром оно ключевое, и должно быть уникальным:
CREATE TABLE Names ( idName int, vcName varchar(50), vcLastName varchar(50), vcSurName varchar(50), dBirthDay datetime, CONSTRAINT cn_unique UNIQUE (idName) )Недавно мне пришлось разрабатывать базу данных, в которой формировалась отчетность из списка клиентов организации. Когда я начал работать с базой данных, то оказалось, что в базе есть дубликаты клиентов. В старой базе данных не было проверок на двойственность записей, только не эффективные проверки в пользовательской программе.
Самая простая проверка на двойников проходила выявлением записей, где полностью совпадали ФИО и дата рождение клиента. Вероятность совпадения всех этих параметров внутри одного города стремиться к нулю. Таким образом, необходимо было всего лишь добавить ограничение, при котором сочетание из этих полей было уникальным. Это можно сделать следующим образом:
CREATE TABLE Names ( idName int , vcName varchar(50), vcLastName varchar(50), vcSurName varchar(50), dBirthDay datetime, CONSTRAINT cn_unique UNIQUE (vcName, vcLastName, vcSurName, dBirthDay) )В данном примере создается таблица из полей: идентификатора, имени, фамилии, отчества и даты рождения. После этого, создается ограничение, при этом оно имеет тип не CHECK, а UNIQUE. В скобках указываются поля, которые должны быть уникальными. В данном примере, я перечислил поля фамилия, имя, отчество и дата рождения. Таким образом, база данных не позволит создать дубликат записи.
Создание ограничений уникальности
Вы можете создать уникальное ограничение в SQL Server с помощью SQL Server Management Studio или Transact-SQL, чтобы не вводить повторяющиеся значения в определенных столбцах, которые не участвуют в первичном ключе. Создание ограничения уникальности автоматически приводит к созданию соответствующего уникального индекса.
Сведения об уникальных ограничениях в Azure Synapse Analytics см. в статье "Первичный ключ", "Внешний ключ" и уникальный ключ в Azure Synapse Analytics.
Разрешения
Требуется разрешение ALTER на таблицу.
Использование SQL Server Management Studio (SSMS)
Создание уникального ограничения с помощью SSMS
- В обозревателе объектовщелкните правой кнопкой мыши таблицу, в которую необходимо добавить ограничение уникальности, и выберите Конструктор.
- В меню Конструктор таблиц выберите пункт Индексы и ключи.
- В диалоговом окне Индексы и ключи нажмите Добавить.
- В сетке в разделе Общие щелкните Тип и выберите Уникальный ключ в раскрывающемся списке справа от свойства, а затем щелкните Закрыть.
- В меню "Файл" выберите "Сохранитьимя таблицы".
Использование Transact-SQL
Создание уникального ограничения с помощью Transact-SQL
- В обозревателе объектов подключитесь к экземпляру ядра СУБД.
- На стандартной панели выберите пункт Создать запрос.
- Скопируйте приведенный ниже пример в окно запроса и нажмите кнопку Выполнить. В этом примере создаются таблица TransactionHistoryArchive4 и ограничение уникальности в столбце TransactionID .
USE AdventureWorks2022; GO CREATE TABLE Production.TransactionHistoryArchive4 ( TransactionID int NOT NULL, CONSTRAINT AK_TransactionID UNIQUE(TransactionID) ); GO
Создание ограничения уникальности в существующей таблице
- В обозревателе объектов подключитесь к экземпляру ядра СУБД.
- На стандартной панели выберите пункт Создать запрос.
- Скопируйте приведенный ниже пример в окно запроса и нажмите кнопку Выполнить. В этом примере создается ограничение уникальности в столбцах PasswordHash и PasswordSalt в таблице Person.Password .
USE AdventureWorks2022; GO ALTER TABLE Person.Password ADD CONSTRAINT AK_Password UNIQUE (PasswordHash, PasswordSalt); GO
Создание ограничения уникальности в новой таблице
- В обозревателе объектов подключитесь к экземпляру ядра СУБД.
- На стандартной панели выберите пункт Создать запрос.
- Скопируйте приведенный ниже пример в окно запроса и нажмите кнопку Выполнить. В этом примере создается таблица и определяется ограничение уникальности в столбце TransactionID .
USE AdventureWorks2022; GO CREATE TABLE Production.TransactionHistoryArchive2 ( TransactionID int NOT NULL, CONSTRAINT AK_TransactionID UNIQUE(TransactionID) ); GO
Следующие шаги
- Инструкция ALTER TABLE (Transact-SQL)
- CREATE TABLE (Transact-SQL)
- table_constraint (Transact-SQL)