用于检查具有软删除的表的复杂唯一性的触发器

问题描述 投票:0回答:1

我有一个表格,其中包含有关各个地区许可证的信息。

它具有唯一的身份主键,以及一个两列的唯一键,即应用程序的许可证号和区域ID。

还支持软删除;当删除一行时,它只是设置一个位字段。

我正在努力确保数据库的唯一性。

无法使用 UNIQUE 约束,因为如果删除的项目使用许可证号,则会失败。 找不到使用检查约束的方法,因为我需要运行查询来检查重复项。

我已经通过触发器做到了这一点:

        create table Test (
            ID int identity primary key,
            PN nvarchar(25),
            RID int,
            Deleted bit default(0)
        )
        go
        create trigger T_PN on Test instead of insert 
        as
        begin
            declare @pn nvarchar(25), @rid int
            select @pn = PN, @rid = RID from inserted 

            if exists(select ID from Test where PN = @pn and RID = @rid and Deleted = 0)
                RAISERROR('PN in use', 11, 1);
            else
                insert into Test select * from inserted
        end

但是,由于身份列的原因,这不起作用;

An explicit value for the identity column in table 'Test' can only be specified when a column list is used and IDENTITY_INSERT is ON.

该项目是一个正在进行的快速开发项目;该表会定期更改,因此不能选择对列名称进行硬编码。

我不知道在触发器中使用

IDENTITY_INSERT
的后果,感觉很古怪且危险?

我倾向于应用程序层解决方案,但我错过了什么吗?

sql-server triggers sql-server-2017
1个回答
2
投票

改用过滤的唯一索引(

UNIQUE CONSTRAINT
在其定义中不支持
WHERE
子句):

CREATE TABLE dbo.Test (TestID int IDENTITY CONSTRAINT PK_test PRIMARY KEY, --ALWAYS name your constraints
                       PN nvarchar(25) NOT NULL,
                       RID int NOT NULL,
                       Deleted bit NOT NULL CONSTRAINT DF_Test_Deleted DEFAULT(0)); ---ALWAYS name your constraints

GO

CREATE UNIQUE INDEX IX_Test_PN_RID_Active ON dbo.Test (PN,RID) WHERE Deleted = 0;

那么你就不能

INSERT
(或
UPDATE
)一行与现有的“未删除”行相同:

--All work
INSERT INTO dbo.Test (PN,RID)
VALUES(N'abc',2),
      (N'abc',3),
      (N'def',3),
      (N'xyz',99);
GO
--Fails
INSERT INTO dbo.Test (PN,RID)
VALUES(N'abc',2);
GO
SELECT *
FROM dbo.Test;
GO

UPDATE dbo.Test
SET Deleted = 1
WHERE TestID = 1;
GO
--Now succeeds
INSERT INTO dbo.Test (PN,RID)
VALUES(N'abc',2);
GO
--Fails, it's the same as the row we just inserted
UPDATE dbo.Test
SET RID = 2
WHERE TestID = 2;
GO

SELECT *
FROM dbo.Test;
GO
--Clean up
DROP TABLE dbo.Test;
© www.soinside.com 2019 - 2024. All rights reserved.