【问题标题】:How do I have a check constraint that refers to another table?我如何有一个引用另一个表的检查约束?
【发布时间】:2010-09-16 15:43:01
【问题描述】:

我在 SQL Server 2008 数据库中有以下表:

  • tblItem,有一个ItemID字段;

  • tblGoodItem,也有ItemID字段,外键指向tblItem;

  • tblBadItem,也有一个ItemID字段,还有一个外键指向tblItem。

一件物品不能既是好物又是坏物;它必须是一个或另一个。但是,无论物品的好坏,它都必须是一个物品。

我的问题是:如何向 tblGoodItem 和 tblBadItem 中的 ItemID 字段添加约束,以使两个表中都不能存在 ItemID 值

我在 Stack Overflow 上阅读了一些关于类似问题的回复,我正在考虑这个解决方案:

  • 创建一个视图 vwItem,它将 tblGoodItem on tblBadItem on ItemID 连接起来。

  • 编写一个 UDF fnItem,它对 vwItem 进行查询以查看视图中存在多少条记录。

  • 有一个调用 fnItem 并验证返回值是否为 0 的约束。

这是最好的主意吗?有人有更好的主意吗?

【问题讨论】:

    标签: sql sql-server tsql sql-server-2008 check-constraints


    【解决方案1】:

    添加一列 tblItem.ItemType 列。此列在任何给定行上只能有一个值(显然)。在 ItemID,ItemType 上添加唯一约束。

    现在的诀窍:很少有人记得这一点,但外键可以引用唯一约束的列。

    CREATE TABLE tblItem (
      ItemID INT PRIMARY KEY,
      ItemType CHAR(1),
      UNIQUE KEY (ItemID, ItemType)
    );
    
    CREATE TABLE tblGoodItem (
      ItemID INT PRIMARY KEY,
      ItemType CHAR(1),
      CHECK (ItemType='G')
      FOREIGN KEY (ItemID, ItemType) REFERENCES tblItem(ItemID, ItemType) 
    );
    
    CREATE TABLE tblBadItem (
      ItemID INT PRIMARY KEY
      ItemType CHAR(1),
      CHECK (ItemType='B')
      FOREIGN KEY (ItemID, ItemType) REFERENCES tblItem(ItemID, ItemType) 
    );
    

    如果将每个子表中的 ItemType 限制为固定值,则 tblItem 中的给定行只能由一个子表引用。

    不过,将物品从好转坏需要三个步骤:

    1. 从 tblGoodItem 中删除行
    2. 在 tblItem 中更新行的 ItemType
    3. 在 tblBadItem 中插入行

    【讨论】:

    • 你领先我几秒。
    • 我非常喜欢这个想法。无需担心从坏到好的变化——事实上,我特别不希望发生这样的变化。
    【解决方案2】:

    去掉 tblGoodItem 和 tblBadItem 并创建一个 ItemType="G" 或 "B" 的新表,并在 ItemID 上放置一个唯一索引或键,然后对 tblItem 没有约束。

    【讨论】:

    • 恐怕不行。好物品有一些坏物品没有的属性,反之亦然。
    • 如果只有几个,在 10 个左右的差异下,那么我仍然将它们组合起来,让它们允许 NULL 并添加一个检查约束以根据 Type 列要求它们
    【解决方案3】:

    我可能在这里不了解您的业务需求,但您为什么希望有一个单独的表来区分好商品和坏商品?这些不是对同一事物的抽象吗?

    为什么不使用 isBadItem 标志或更具体地说是 itemConditionStatus 列。

    【讨论】:

      【解决方案4】:

      在 tblItem 中,添加 itemType 列。有一个检查约束来确保 itemType 是好是坏。在 (ItemID, itemType ) 上创建唯一约束

      将 itemType 列添加到 bad 和 good items 表中。有一个检查约束来确保 itemType 在好表中是好的,在坏表中是坏的。

      【讨论】:

        【解决方案5】:

        您不能在 CHECK 约束中使用 SELECT 语句 - 这并不是它们的真正目的。

        我认为您最好的选择是在 ItemId 中编写一个 UDF 传递并检查它是否存在。对于这种情况,它确实是最简单的选择。

        我添加了一些测试数据和一个示例函数。

        CREATE FUNCTION dbo.fn_CheckItems(@itemId INT) RETURNS BIT
        
        AS BEGIN
        
        DECLARE @i INT,
                @rv BIT
        
        
        SET @i = 0
        
        IF (SELECT COUNT(*) FROM tblBadItem WHERE ItemId = @ItemId) > 0
        BEGIN
        SET @i = 1
        END
        
        
        IF (SELECT COUNT(*) FROM tblGoodItem WHERE ItemId = @ItemId) > 0
        BEGIN
        SET @i = @i + 1
        END
        
        IF (@i > 1)
        BEGIN
            SET @rv = 1
        END
        ELSE
        BEGIN
            SET @rv =0
        END
        
        
        RETURN @rv
        
        END
        GO
        
        CREATE  TABLE tblItem (
          ItemID INT IDENTITY(1,1) PRIMARY KEY,
          DateAdded DATETIME
        )
        GO
        
        CREATE TABLE tblGoodItem (
          ItemID INT PRIMARY KEY,
          CHECK (dbo.fn_CheckItems(ItemId) = 0)
        
        )
        GO
        
        CREATE TABLE tblBadItem (
          ItemID INT PRIMARY KEY,
          CHECK (dbo.fn_CheckItems(ItemId) = 0)
        )
        GO
        
        INSERT INTO tblItem (DateAdded)
        VALUES (GETDATE())
        
        INSERT INTO tblGoodItem(ItemID)
        SELECT ItemId FROM tblItem
        
        --This statement will fail as the ItemId is already in GoodItems
        INSERT INTO tblBadItem(ItemID)
        SELECT ItemId FROM tblItem
        
        
        DROP TABLE tblItem
        DROP TABLE tblGoodItem
        DROP TABLE tblBadItem
        DROP FUNCTION dbo.fn_CheckItems
        

        【讨论】:

        • @AlexKuznetsov - 你能否详细说明为什么这不起作用。我创建了一个测试,它工作正常?
        • 抱歉,我错了。但是,这样的 UDF 非常慢,如果您使用快照隔离,您可能会在并发下偶尔得到错误的结果。
        • @AlexKuznetsov - 没问题。我必须承认我在使用快照复制时没有使用过这个,所以我不能不同意或同意。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-01-28
        • 2020-12-03
        • 1970-01-01
        • 2021-05-08
        • 2019-10-14
        • 1970-01-01
        相关资源
        最近更新 更多