【问题标题】:Add unique constraint to combination of two columns为两列的组合添加唯一约束
【发布时间】:2013-03-25 21:08:03
【问题描述】:

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

例如,字段是:

ID  
Name  
Active  
PersonNumber  

我只想要 1 条具有唯一 PersonNumber 且 Active = 1 的记录。
(所以两个字段的组合需要是唯一的)

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

【问题讨论】:

  • 您仍然需要在应用程序代码中担心它。
  • 对,“不用担心”是什么意思?如果用户尝试插入重复项,而 SQL Server 没有这样做,您不想告诉他们吗?听起来应用程序需要担心它。

标签: sql sql-server


【解决方案1】:

删除重复项后:

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

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

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

如果您想防止异常冒泡到应用程序,而不对应用程序进行更改,您可以使用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”中插入重复键。 声明已终止。

我最近还写了一篇关于将唯一约束应用于两列的解决方案以任意顺序

【讨论】:

  • @leora 是的,我很确定我的答案涉及两个的唯一性。
  • 并不是每列都必须是唯一的,而是列的组合(连接)必须是唯一的。那有意义吗 。 .
  • @AaronBertrand 感谢您的回复。想象一下我的 2 个字段是 2 个用户 ID(我正在创建一个聊天,一个聊天有 2 个用户)。如何使用 UNIQUE(column1, column2);但也以另一种方式工作?像 UNIQUE(column1,column2)和 UNIQUE(column2,column1)。有什么建议吗?非常感谢。
  • @Ricardo 它们只是代表聊天中任意两个用户的两列吗?或者一列是否具有相关性(如聊天发起者)?如果顺序无关紧要,那么您还可以使用逻辑来确保 user1 u1c AS CASE WHEN user1 < user2 THEN user1 ELSE user2 END 和 u2c AS CASE WHEN user1 &lt; user2 THEN user2 ELSE user1 END
  • @Ricardo 我在 2 月份写了一篇关于这个的文章,抱歉我忘记在它发表后回来联系你:Enforce a Unique Constraint Where Order Does Not Matter
【解决方案2】:

这也可以在 GUI 中完成:

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

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

  1. 检查要包含的列

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

【讨论】:

  • 唯一约束和唯一索引有什么区别?因为当你设置唯一约束时,它有 900 字节的限制,但看起来唯一索引没有。
  • Nothing 参考这篇文章:blog.sqlauthority.com/2007/04/26/…
  • 我的new Index 不可点击它已被禁用:(
  • @Faisal 关闭您为该表打开的结果/设计窗口,然后重试。
  • @Faisal 检查这个:stackoverflow.com/a/60014466/4654957
【解决方案3】:

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

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

【讨论】:

    【解决方案4】:

    在我的情况下,我需要允许许多非活动键,并且只有两个键的一个组合处于活动状态,如下所示:

    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 答案采用的最佳做法。
    猜你喜欢
    • 2012-05-17
    • 1970-01-01
    • 2011-03-23
    • 2017-04-23
    • 1970-01-01
    • 2020-01-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多