SQL Server,自引用数据,如何为此添加约束

时间:2013-05-23 14:06:59

标签: sql sql-server constraints

想象一下,我有以下结构:

DECLARE @Products TABLE (
    MemberId INT,
    ProductId INT,
    GlobalProductId INT,
    PRIMARY KEY (MemberId, ProductId));
INSERT INTO @Products VALUES (1, 1, NULL);--this is my "global product"
INSERT INTO @Products VALUES (2, 1, NULL);--this is okay
INSERT INTO @Products VALUES (2, 2, 1);--this is okay
INSERT INTO @Products VALUES (2, 3, 2);--this should fail
SELECT * FROM @Products;

我想强制执行的规则是,MemberId = 1持有全球产品,而所有其他MemberId持有普通产品。一组普通产品可以链接到一个全球产品。

所以我希望会员的产品能够链接到全局产品,即存在外键约束,如果GlobalProductId不为NULL,则应该存在与GlobalProductId匹配的ProductId,其中MemberId = 1。

在上面的示例中,我有一个ProductId = 1的全局产品。然后我创建了三个普通产品:

  • 第一个没有全球产品;
  • 第二个链接到我之前创建的单个全球产品(然后我可以将更多产品链接到同一个全球产品);
  • 第三个应该失败,因为我已将其链接到不存在的全局产品,即此脚本将不返回任何内容:

    SELECT * FROM @Products WHERE MemberId = 1 AND ProductId = 2;
    

我可以看到,最简单的解决方案是创建一个新表,只保留全球产品。这种方法的问题在于我有一整套例程来加载,更新,删除Product表中的数据,以及第二组例程来执行同一个表中的计算等。如果我要介绍一个新的“全局产品”表,那么我将不得不复制几十个UDF来实现这一点,我的代码将变得更加复杂。

2 个答案:

答案 0 :(得分:2)

添加一个固定为1的计算列,然后添加一个外键:

CREATE TABLE Products (
    MemberId INT,
    ProductId INT,
    GlobalProductId INT,
    PRIMARY KEY (MemberId, ProductId),
    GlobalMemberId AS 1 PERSISTED,
    FOREIGN KEY (GlobalMemberId,GlobalProductID)
        references Products (MemberId,ProductID)
    );
INSERT INTO Products VALUES (1, 1, NULL);--this is my "global product"
INSERT INTO Products VALUES (2, 1, NULL);--this is okay
INSERT INTO Products VALUES (2, 2, 1);--this is okay
INSERT INTO Products VALUES (2, 3, 2);--this should fail
SELECT * FROM Products;

这会产生以下结果:

  

Msg 547,Level 16,State 0,Line 1

     

INSERT语句与FOREIGN KEY SAME TABLE约束“FK__Products__7775B2CE”冲突。冲突发生在数据库“abc”,表“dbo.Products”。

     

声明已经终止。

MemberId    ProductId   GlobalProductId GlobalMemberId
----------- ----------- --------------- --------------
1           1           NULL            1
2           1           NULL            1
2           2           1               1

答案 1 :(得分:0)

为什么不添加CHECK约束:

ALTER TABLE Products ADD CONSTRAINT CHK_ColumnD_GlobalProductId 
CHECK (GlobalProductId IS NULL AND MemberId = 1 
       OR GlobalProductId IS NOT NULL AND MemberId != 1);

FOREIGN KEY

ALTER TABLE Products ADD CONSTRAINT fk_SelfProducts
FOREIGN KEY (GlobalProductId )
REFERENCES Products (ProductId)