我有一个表格,其中包含有关各个地区许可证的信息。
它具有唯一的身份主键,以及一个两列的唯一键,即应用程序的许可证号和区域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
的后果,感觉很古怪且危险?
我倾向于应用程序层解决方案,但我错过了什么吗?
改用过滤的唯一索引(
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;