我有一个表,不知怎的,同一个人两次进入了我的person表。现在,主键只是一个自数但还有另外两个字段我想强制它们是唯一的。

例如,字段为:

ID  
Name  
Active  
PersonNumber  

我只想要一个独特的PersonNumber和活动= 1的记录。 (所以两个字段的组合需要是唯一的)

在SQL server的现有表上,最好的方法是什么,我可以让它,如果任何人用与现有值相同的值进行插入,它会失败,这样我就不必在我的应用程序代码中担心这个问题。


当前回答

如果你有很多插入查询,但不想每次都发出错误消息,你可以这样做:

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

其他回答

这也可以在GUI中完成:

在表格“Person”下,右键单击Indexes 单击/悬停新建索引 单击“非聚集索引…”

将给出一个默认的索引名称,但您可能希望更改它。 “唯一”复选框 单击Add……按钮

检查您希望包含的列

在每个窗口中单击OK。

在我的情况下,我需要允许许多非活动,只有一个组合的两个键活动,就像这样:

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

如果你有很多插入查询,但不想每次都发出错误消息,你可以这样做:

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

删除副本后:

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

or

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

当然,在SQL Server尝试插入行并返回异常(异常代价很高)之前,最好先检查这种违反。

不同错误处理技术对性能的影响 在进入TRY/CATCH之前检查潜在的约束违反

如果你想防止异常冒泡到应用程序,而不需要对应用程序进行更改,你可以使用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

最后一次插入时的错误信息:

Msg 2627, 14层,状态1,3线 违反UNIQUE KEY约束'uq_Person'。不能在对象'dbo.Person'中插入重复的键。 声明已终止。

此外,我最近在博客上写了一个解决方案,以任意顺序对两列应用唯一约束:

在顺序无关紧要的情况下强制执行唯一约束