向两个列的组合添加唯一约束


149

我有一张桌子,不知何故,同一个人Person两次进入了我的桌子。现在,主键只是一个自动编号,但是还有两个我想强制保持唯一性的字段。

例如,这些字段是:

ID  
Name  
Active  
PersonNumber  

我只希望1条记录具有唯一的PersonNumber和Active =1。
(因此,两个字段的组合必须是唯一的)

在SQL Server中现有表上的最佳方法是什么,我可以做到这一点,因此,如果其他人使用与现有值相同的值进行插入,则插入失败,因此我不必在应用程序代码中担心这一点。


3
您仍然需要在应用程序代码中担心它。
Dan Bracuk

2
对,“不必担心”意味着什么?如果用户尝试插入重复项,而SQL Server不这样做,您是否不想告诉他们?听起来应用程序需要为此担心。
Aaron Bertrand

Answers:


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

但是,如果您不告诉用户他们没有执行插入操作,他们会想知道为什么数据不存在并且没有报告异常。


这里的“ 编辑”是一个示例,它可以完全满足您的要求,甚至使用与您的问题相同的名称,也可以对其进行证明。您应该先尝试一下,然后再假设上述想法只处理一栏或另一栏,而不是结合使用...

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是的,我很确定我的回答在两列中涉及唯一性。
Aaron Bertrand

2
不是每个列都必须是唯一的,而是列的组合(串联)必须是唯一的。那有意义吗 。。
leora

14

这也可以在GUI中完成:

  1. 在“人员”表下,右键单击“ 索引”
  2. 点击/悬停新索引
  3. 单击非聚集索引...

在此处输入图片说明

  1. 将提供默认的索引名称,但您可能需要更改它。
  2. 选中唯一复选框
  3. 单击添加...按钮

在此处输入图片说明

  1. 检查您要包含的列

在此处输入图片说明

  1. 在每个窗口中单击确定

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

感谢您在此处包括您的测试用例。这是最佳做法,我希望看到更多采用Stack Overflow的答案。
杰里米·卡尼

0

而且,如果您有很多插入查询,但每次都不希望收到错误消息,则可以执行以下操作:

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

在此处输入图片说明

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.