Sql server 是否可以在SQL Server 2012中添加约束,以防止在软删除旧数据之前输入新数据?
我有下表:Sql server 是否可以在SQL Server 2012中添加约束,以防止在软删除旧数据之前输入新数据?,sql-server,tsql,Sql Server,Tsql,我有下表: CREATE TABLE [Test] ( [Id] BIGINT IDENTITY(1, 1) NOT NULL, [Name] CHARACTER VARYING(255) NOT NULL, [DeletedOn] DATETIMEOFFSET NULL, UNIQUE([Name], [DeletedOn]), PRIMARY KEY([Id]) ); GO 我插入一个新记录,如下所示: INSERT INTO [Test] (
CREATE TABLE [Test]
(
[Id] BIGINT IDENTITY(1, 1) NOT NULL,
[Name] CHARACTER VARYING(255) NOT NULL,
[DeletedOn] DATETIMEOFFSET NULL,
UNIQUE([Name], [DeletedOn]),
PRIMARY KEY([Id])
);
GO
我插入一个新记录,如下所示:
INSERT INTO [Test] (Name, DeletedOn) VALUES ('A record', SYSDATETIMEOFFSET())
使用此命令的后续插入将按预期完成。但是,我想要求在使用DeletedOn
值比具有相同Name
的现有记录更新的数据之前,只要表中存在具有相同名称
的记录且该记录具有NULL
DeletedOn
值,则拒绝该数据
另一种解释这种行为的方法是让您想象一个用户密码历史记录。用户输入密码,我的软件将其散列并存储在数据库中。用户密码过期且DeletedOn
设置为当前日期和时间。我不希望用户再次输入相同的密码,所以这就是保存历史记录的目的。为了保持数据一致性,我想防止在已存在一个活动密码且该密码在DeletedOn
列中没有值时添加密码。因此,如果我的软件出现错误行为,并试图将随机密码添加到用户的密码历史记录中,它应该会失败,因为它会违反某些约束,阻止删除不是单个活动密码的密码
我最初设想,如果尝试这种行为,我会将此逻辑封装在存储过程中并抛出一个错误,但我很好奇这是否可以用另一种方式完成。据我所知,在软删除旧数据之前,没有现成的功能来阻止输入新数据?我们需要增加一些限制来防止这种情况。我们可以使用check约束来验证Name和Deleted On的组合是否存在 试试这个
CREATE FUNCTION [dbo].[chk_RecordExists](@Name Varchar(255))
RETURNS INT
AS
BEGIN
DECLARE @Result int
SET @Result = 0
DECLARE @id AS INT
SET @id=0
SELECT @id=MAX(ID) FROM [Test] WHERE Name = @Name
IF (@id=0)
BEGIN
SET @Result = 1 -- Allow to insert as its New record
END
ELSE
BEGIN
-- Check the latest record for name if Deletedon is not null then its soft deleted can allow to insert new
IF EXISTS (SELECT ID FROM [Test] WHERE ID=@id AND Name = @Name AND DeletedOn IS NOT NULL)
BEGIN
SET @Result = 1 -- Allow to insert as its old data is soft deleted
END
END
RETURN @Result
END
GO
ALTER TABLE Test WITH NOCHECK ADD CONSTRAINT [chk_Constraint] CHECK(dbo.chk_RecordExists](Name)=0)
GO
我从来没有使用过用户
而不是关键字,但我已经读到它可以用来做你想做的事情。下面是来自MSDN的示例代码,以防止在有旧记录时插入新记录。在而不是
将上面的触发器更改为仅插入唯一行
CREATE TRIGGER IO_Trig_INS_TEST ON MyTestTable
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON
IF (
NOT EXISTS ( SELECT P.SSN
FROM MyTestTable P
,inserted I
WHERE P.SSN = I.SSN ) )
INSERT INTO MyTestTable
SELECT FirstName
,LastName
,SSN
FROM ( SELECT FirstName
,LastName
,SSN
,ROW_NUMBER() OVER ( PARTITION BY SSN ORDER BY LastName, Firstname ) AS ROWNUM
FROM INSERTED ) A
WHERE A.ROWNUM = 1
ELSE
RAISERROR(N'You are trying to insert duplicate records',16,1)
END
真遗憾,扳机坏了。它避免了常见的prat Fall(即许多人编写的中断触发器假定插入的只包含一行),但随后由于不检查插入的本身是否,包含重复的值。@Damien_不信者我已修改了答案,使其包含只插入唯一值的触发器。这非常简单,可将数据完整性检查保持在尽可能低的级别。我从来不知道你能在函数中定义复杂的检查。@MitchWheat,这是我在你的问题中看到的第二条评论,几乎没有解释。你能详细说明一下这是怎么打破的吗?我承认,我重写了这个实现来为我工作,并且从未测试过原始版本。然而,这个概念似乎相当可靠。你对此有何看法?评论是针对一个答案的。这是对你的一个问题的回答,这是巧合。在选择最大值并在其可能发生更改时使用它时,没有任何事务处理。这可能不是一个公认的答案,因为它可能会让人们误入歧途。@MitchWheat约束检查不是在SERIALIZABLE
事务级别进行的,以强制执行ACID原则吗?一个INSERT
被认为是一个语句,所有语句都是事务,所有事务都是原子操作。这不是我所指的。从具有活动插入的表中选择“最大值”。稍后从表中选择最大值,其值是否相同?
CREATE TRIGGER IO_Trig_INS_TEST ON MyTestTable
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON
IF (
NOT EXISTS ( SELECT P.SSN
FROM MyTestTable P
,inserted I
WHERE P.SSN = I.SSN ) )
INSERT INTO MyTestTable
SELECT FirstName
,LastName
,SSN
FROM ( SELECT FirstName
,LastName
,SSN
,ROW_NUMBER() OVER ( PARTITION BY SSN ORDER BY LastName, Firstname ) AS ROWNUM
FROM INSERTED ) A
WHERE A.ROWNUM = 1
ELSE
RAISERROR(N'You are trying to insert duplicate records',16,1)
END