【问题标题】:Unique constraint on two columns regardless of order无论顺序如何,两列的唯一约束
【发布时间】:2012-07-10 00:39:45
【问题描述】:

我有如下表定义:

CREATE TABLE [Car] 
(
   CarID int NOT NULL PRIMARY KEY IDENTITY(1,1),
   FirstColorID int FOREIGN KEY REFERENCES Colors(ColorID),
   SecondColorID int FOREIGN KEY REFERENCES Colors(ColorID),

   UNIQUE(FirstColorID, SecondColorID)
)

我希望这两个颜色列是唯一的,无论它们出现在什么组合中。

例如尝试:

INSERT INTO Car (FirstColorID, SecondColorID) VALUES (1, 2); --should succeed

但是在第一个记录存在并且颜色反转后尝试相同应该失败:

INSERT INTO Car (FirstColorID, SecondColorID) VALUES (2, 1); --should violate constraint/check

我已经使用高级代码解决了这个问题,但我宁愿直接在数据库中强制执行约束,最好不涉及触发器之类的东西。

谢谢。

【问题讨论】:

    标签: sql-server unique-constraint


    【解决方案1】:

    您可以创建计算列,例如:

    CREATE TABLE [Car] 
    (
       CarID int NOT NULL PRIMARY KEY IDENTITY(1,1),
       FirstColorID int, --FOREIGN KEY REFERENCES Colors(ColorID),
       SecondColorID int, --FOREIGN KEY REFERENCES Colors(ColorID),
       xColor As Cast(Case When FirstColorID > SecondColorID Then FirstColorID Else SecondColorID End as varChar) + ',' + 
            Cast(Case When FirstColorID <= SecondColorID Then SecondColorID Else FirstColorID End as varChar),
       UNIQUE(xColor)
    )
    

    更新(你应该之前测试一下,我只是快速测试)

    想法:

    整数是 4 个字节。如果我将 2 个整数放在一起 - 我得到 8 个字节。如果我订购它们 - 我会得到唯一的 BigInt 值(8 个字节)。

    那我该怎么办:

    1. 我确保它们的顺序正确
    2. 我将 32 位的字节向左移动(所以只需乘以 4294967296 我就得到了我想要的)
    3. 我进行逻辑 OR- 所以我得到 8 字节 BigInt 值,它应该始终是唯一的!

    所以:

    CREATE TABLE [Car] 
    (
       CarID int NOT NULL PRIMARY KEY IDENTITY(1,1),
       FirstColorID int, --FOREIGN KEY REFERENCES Colors(ColorID),
       SecondColorID int, --FOREIGN KEY REFERENCES Colors(ColorID),
       xColor As 
           Case When FirstColorID > SecondColorID Then 
                Cast(FirstColorID as BigInt) * Cast(4294967296 as BigInt) | Cast(SecondColorID as BigInt)
            Else 
                Cast(SecondColorID as BigInt) * Cast(4294967296 as BigInt) |  Cast(FirstColorID as BigInt)
            End
      UNIQUE(xColor)
    )
    

    【讨论】:

    • 我喜欢这种方法,但我不确定在性能基础上使用计算字符串列。也许可以使用某种数值运算来代替?我想这不能只是将数字相乘,会有各种各样的冲突。
    • 谢谢,这就是我要做的。我喜欢这种方法,因为它在表定义中是完全独立的,并且应该足够高效。
    【解决方案2】:

    通过限制要求SecondColorID &gt;= FirstColorID 来完全避免该问题。它对可以进入数据库的内容造成了限制,这超出了您正在寻找的内容。但是,如果您想查询某种颜色模式,那么这个额外的要求将很有用,因为您可以只查找 SecondColorID = 1 AND FirstColorID = 2 而不必编写额外的逻辑来解释具有不可预测顺序的两个颜色 ID。

    CREATE TABLE [Car] 
    (
       CarID int NOT NULL PRIMARY KEY IDENTITY(1,1),
       FirstColorID int FOREIGN KEY REFERENCES Colors(ColorID),
       SecondColorID int FOREIGN KEY REFERENCES Colors(ColorID),
    
       UNIQUE(FirstColorID, SecondColorID)
    )
    
    ALTER TABLE [Car] WITH CHECK
        ADD CHECK (SecondColorID >= FirstColorID);
    

    【讨论】:

    • 这是一种创造性的方式。但是现在我需要我的高级代码来选择仅用于插入/更新的外键顺序。它比我拥有的要好,但仍然存在相同的基本问题。
    • 更好的选择可能是处理存储过程中的任何参数伪造。
    【解决方案3】:

    创建计算列,使ComputedFirstColorID 是FirstColorID 和SecondColorID 的较低者,ComputedSecondColorID 是FirstColorID 和SecondColorID 的较高者。现在索引

    CREATE UNIQUE NONCLUSTERED INDEX index_name ON Car (ComputedFirstColorID, ComputedSecondColorID)
    

    应该没问题。

    【讨论】:

      【解决方案4】:
      CREATE UNIQUE NONCLUSTERED INDEX index_name ON Car (FirstColorID, SecondColorID)
      

      http://msdn.microsoft.com/en-us/library/ms188783.aspx

      【讨论】:

      • 这将阻止(1,2) 的两个条目 - 但是当(1,2) 已经在表中时,此约束不会阻止输入(2,1)... ..
      猜你喜欢
      • 2017-07-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多