Додайте унікальне обмеження до комбінації двох стовпців


149

У мене стіл і якось одна і та сама людина потрапила до мого Personстолу двічі. На даний момент первинний ключ - це лише авточисло, але існують ще два поля, які я хочу змусити бути унікальними.

Наприклад, поля:

ID  
Name  
Active  
PersonNumber  

Я хочу лише 1 запис з унікальним PersonNumber та Active = 1.
(Отже, комбінація двох полів повинна бути унікальною)

Який найкращий спосіб у існуючій таблиці на SQL-сервері я можу зробити так, якщо хтось інший зробить вставку з тим же значенням, що і існуюче значення, це не вдасться, тому мені не доведеться турбуватися про це в коді програми.


3
Ви все ще повинні турбуватися про це у коді програми.
Дан Брачук

2
Правильно, "не треба про це турбуватися", що означає? Якщо користувач намагається вставити дублікат, а SQL Server цього не робить, ви не хочете їм це сказати? Здається, що програма повинна турбуватися про це.
Аарон Бертран

Відповіді:


219

Після видалення дублікатів:

ALTER TABLE dbo.yourtablename
  ADD CONSTRAINT uq_yourtablename UNIQUE(column1, column2);

або

CREATE UNIQUE INDEX uq_yourtablename
  ON dbo.yourtablename(column1, column2);

Звичайно, часто може бути краще спочатку перевірити це порушення, перш ніж просто дозволити SQL Server спробувати вставити рядок і повернути виняток (винятки дорогі).

http://www.sqlperformance.com/2012/08/t-sql-queries/error-handling

http://www.mssqltips.com/sqlservertip/2632/checking-for-potential-constraint-violations-before-entering-sql-server-try-and-catch-logic/

Якщо ви хочете не допустити, щоб винятки перетворювались на додаток, не вносячи змін у додаток, ви можете скористатися INSTEAD OFтригером:

CREATE TRIGGER dbo.BlockDuplicatesYourTable
 ON dbo.YourTable
 INSTEAD OF INSERT
AS
BEGIN
  SET NOCOUNT ON;

  IF NOT EXISTS (SELECT 1 FROM inserted AS i 
    INNER JOIN dbo.YourTable AS t
    ON i.column1 = t.column1
    AND i.column2 = t.column2
  )
  BEGIN
    INSERT dbo.YourTable(column1, column2, ...)
      SELECT column1, column2, ... FROM inserted;
  END
  ELSE
  BEGIN
    PRINT 'Did nothing.';
  END
END
GO

Але якщо ви не скажете користувачеві, що вони не виконували вставку, вони зададуться питанням, чому даних немає, і не було повідомлено про виключення.


Ось EDIT - це приклад, який робить саме те, що ви просите, навіть використовуючи ті самі назви, що і ваше запитання, і доводить це. Ви повинні спробувати це, перш ніж припускати, що вищезазначені ідеї стосуються лише одного стовпчика чи іншого, на відміну від комбінації ...

USE tempdb;
GO

CREATE TABLE dbo.Person
(
  ID INT IDENTITY(1,1) PRIMARY KEY,
  Name NVARCHAR(32),
  Active BIT,
  PersonNumber INT
);
GO

ALTER TABLE dbo.Person 
  ADD CONSTRAINT uq_Person UNIQUE(PersonNumber, Active);
GO

-- succeeds:
INSERT dbo.Person(Name, Active, PersonNumber)
  VALUES(N'foo', 1, 22);
GO

-- succeeds:
INSERT dbo.Person(Name, Active, PersonNumber)
  VALUES(N'foo', 0, 22);
GO

-- fails:
INSERT dbo.Person(Name, Active, PersonNumber)
  VALUES(N'foo', 1, 22);
GO

Дані в таблиці після всього цього:

ID   Name   Active PersonNumber
---- ------ ------ ------------
1    foo    1      22
2    foo    0      22

Повідомлення про помилку на останній вставці:

Повідомлення 2627, рівень 14, стан 1, рядок 3 Порушення єдиного обмеження ключа «uq_Person». Неможливо вставити повторюваний ключ в об’єкт 'dbo.Person'. Заява скасована.


3
@leora так, я впевнений, що моя відповідь стосується унікальності у двох стовпцях .
Аарон Бертран

2
не те, що кожен стовпець повинен бути унікальним, його поєднання (конкатенація) стовпців має бути унікальним. Чи має це сенс . .
leora

14

Це також можна зробити в графічному інтерфейсі:

  1. Під таблицею "Особа" натисніть правою кнопкою миші Індекси
  2. Клацніть / наведіть курсор Новий індекс
  3. Натисніть Індекс без кластера ...

введіть тут опис зображення

  1. Буде вказано ім'я за замовчуванням, але ви можете змінити його.
  2. Поставте прапорець Унікальний
  3. Натисніть Додати ... кнопку

введіть тут опис зображення

  1. Перевірте стовпці, які ви хочете включити

введіть тут опис зображення

  1. Клацніть ОК у кожному вікні.

1
яка різниця між унікальним обмеженням та унікальним індексом? Тому що, коли ви встановлюєте унікальний обмеження, він має обмеження 900 байтів, але схоже, що унікальний індекс не має.
batmaci

2
Нічого Не дивіться цю статтю для довідки: blog.sqlauthority.com/2007/04/26/…
Ілі

Моє new Indexнеможливість натискання, його відключено :(
Faisal

4
@Faisal закрийте вікна результатів / дизайну, які ви відкрили для цієї таблиці, і спробуйте ще раз.
KalaNag


3

У моєму випадку мені потрібно було дозволити багато неактивних та лише одну комбінацію двох клавіш, як це:

UUL_USR_IDF  UUL_UND_IDF    UUL_ATUAL
137          18             0
137          19             0
137          20             1
137          21             0

Це, здається, працює:

CREATE UNIQUE NONCLUSTERED INDEX UQ_USR_UND_UUL_USR_IDF_UUL_ATUAL
ON USER_UND(UUL_USR_IDF, UUL_ATUAL)
WHERE UUL_ATUAL = 1;

Ось мої тестові випадки:

SELECT * FROM USER_UND WHERE UUL_USR_IDF = 137

insert into USER_UND values (137, 22, 1) --I CAN NOT => Cannot insert duplicate key row in object 'dbo.USER_UND' with unique index 'UQ_USR_UND_UUL_USR_IDF_UUL_ATUAL'. The duplicate key value is (137, 1).
insert into USER_UND values (137, 23, 0) --I CAN
insert into USER_UND values (137, 24, 0) --I CAN

DELETE FROM USER_UND WHERE UUL_USR_ID = 137

insert into USER_UND values (137, 22, 1) --I CAN
insert into USER_UND values (137, 27, 1) --I CAN NOT => Cannot insert duplicate key row in object 'dbo.USER_UND' with unique index 'UQ_USR_UND_UUL_USR_IDF_UUL_ATUAL'. The duplicate key value is (137, 1).
insert into USER_UND values (137, 28, 0) --I CAN
insert into USER_UND values (137, 29, 0) --I CAN

Дякую вам, включивши сюди ваш тестовий випадок. Це найкраща практика, яку я хотів би прийняти більше відповідей на переповнення стека.
Джеремі Кейні

0

І якщо у вас є багато вставних запитів, але ви не хочете отримувати повідомлення про помилку щоразу, ви можете це зробити:

CREATE UNIQUE NONCLUSTERED INDEX SK01 ON dbo.Person(ID,Name,Active,PersonNumber) 
WITH(IGNORE_DUP_KEY = ON)

введіть тут опис зображення

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.