【问题标题】:SQL Server check constraint to prevent specific duplicatesSQL Server 检查约束以防止特定重复
【发布时间】:2015-09-16 21:16:31
【问题描述】:

我有这样的数据结构:

CREATE TABLE test_Bundles 
(
   BundleId INT,
   UserId INT,
   status INT
)

INSERT INTO test_Bundles VALUES (1, 1, 1)
INSERT INTO test_Bundles VALUES (2, 1, 3)
INSERT INTO test_Bundles VALUES (3, 1, 3)
INSERT INTO test_Bundles VALUES (4, 2, 1)
INSERT INTO test_Bundles VALUES (5, 2, 3)
INSERT INTO test_Bundles VALUES (6, 2, 3)
GO

一个用户只能拥有一个状态=1 的捆绑包。但他们可以有很多 status=2 或 status=3 或 status=4。

谁能想到在 SQL Server 中强制执行此规则的方法?

【问题讨论】:

    标签: sql-server indexing constraints


    【解决方案1】:

    好吧,您可以自然地使用触发器,也可以使用唯一过滤索引(如果您运行的是 SQL Server 2008 或更高版本),例如:

    create unique index ix_tmp 
    on test_Bundles (UserId, status) where status = 1;
    

    如果您更喜欢使用触发路由(适用于任何合理版本的 SQL Server),它看起来像这样:

    create trigger tgrTmp on test_Bundles for insert, update 
    as
    begin;
    
    if exists(  select  * 
                from test_Bundles t
                join inserted i 
                on t.UserId = i.UserId
                and t.BundleId != i.BundleId
                where t.status = 1 
                and i.status = 1)
    begin;
        raiserror ('unique violation',16,1);
        rollback;
    end;
    
    end;
    

    【讨论】:

    • 只有当检查约束选项不存在时才应该选择触发器,例如MySQL。这完全是一个糟糕的建议。
    • @Rahul,在你的回答中看到我的 cmets,检查约束根本不像你想象的那样工作
    • 非常感谢。我不知道过滤索引。我会用那个方法。该应用程序将阻止不良数据,但这有利于保护和性能。
    【解决方案2】:
    create trigger trigPreventStatus1Duplicates
    On test_Bundles for insert, update
    as 
    declare @errMsg varchar(200) = 
         'You cannot enter more than one status 1 bundle per user.'
    set NoCount On
       begin
           if exists (Select * from inserted i 
                        join test_Bundles b 
                           on b.userId = i.userId 
                              and b.bundleId != i.bundleId
                      where b.status = 1
                      group by i.UserId
                      having count(*) > 1)
              begin
                  rollback transaction
                  raiserror(errMsg , 16, 1)
              end  
       end
    

    【讨论】:

    • 我看到这种方法的好处是能够返回自定义消息。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-03
    • 1970-01-01
    • 1970-01-01
    • 2015-08-07
    • 1970-01-01
    • 1970-01-01
    • 2013-11-17
    相关资源
    最近更新 更多