Добавить уникальное ограничение для комбинации двух столбцов


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 Нарушение ограничения UNIQUE KEY 'uq_Person'. Невозможно вставить дубликат ключа в объект «dbo.Person». Заявление было прекращено.


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

2
это не значит, что каждый столбец должен быть уникальным, его комбинация (конкатенация) столбцов должна быть уникальной. Имеет ли это смысл . ,
Леора

14

Это также можно сделать в графическом интерфейсе:

  1. Под таблицей «Персона» щелкните правой кнопкой мыши « Индексы».
  2. Нажмите / наведите новый указатель
  3. Нажмите Некластерный индекс ...

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

  1. Будет дано имя индекса по умолчанию , но вы можете изменить его.
  2. Проверьте уникальный флажок
  3. Нажмите Добавить ... кнопку

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

  1. Проверьте столбцы, которые вы хотите включить

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

  1. Нажмите OK в каждом окне.

1
В чем разница между уникальным ограничением и уникальным индексом? Потому что, когда вы устанавливаете уникальное ограничение, оно имеет ограничение 900 байт, но похоже, что уникальное индексирование не имеет.
batmaci

2
Ничего См. Эту статью для справки: blog.sqlauthority.com/2007/04/26/…
Eli

Мой 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.