确保单个添加到SQL表中

时间:2017-05-25 19:16:31

标签: sql sql-server sqltransaction

我有一个DocumentJob表,其中包含要对给定文档执行的作业。表中的每一行都有jobId,documentId和jobstatus。

多个线程/进程将尝试在任何给定时间添加到此表(使用下面给出的代码)

begin tran
if exists 
(
    select 1 from DocumentJob 
    where DocumentId = @inDocumentId 
    and Status in ('Running', 'New')
)
    throw 50001, 'New or Active Job for Document is already present', 1

insert into DocumentJob (DocumentId, Status) values(DocumentId, 'New')
select @JobId = scope_identity();
commit;

对于给定的文档ID - 我想仅在没有正在运行或新的文档的其他作业时添加新作业。以下代码段是否会处理上述要求,或者是否存在可能违反上述条件的情况?

我的目标是在同时调用上述过程时理解表是否会被正确锁定等。

1 个答案:

答案 0 :(得分:1)

[1] 我要做的第一件事就是创建一个唯一的过滤索引:

CREATE UNIQUE NONCLUSTERED INDEX IUF_DocumentJob_DocumentId
ON dbo.DocumentJob (DocumentId)
--INCLUDE (...)
WHERE Status in ('Running', 'New')

此独特索引可确保dbo.DocumentJob表格具有以下状态之一的唯一DocumentId'Running''New'

这足以防止状态为dbo.DocumentJob'Running'时出现重复的'New'

[2]在[1]之后,当前问题中包含的源代码可以用简单的

替换
INSERT INTO dbo.DocumentJob (DocumentId, Status) VALUES (@DocumentId, 'New')

对于任何{@DocumentId值以及RunningNew状态},只有第一个INSERT(或UPDATE)执行成功并且接下来执行将失败。

注意:我会将此代码封装在事务(请参阅SET XACT_ABORT ON)和TRY ... CATCH块中(本主题未在此答案中提供)。

[3]为了测试我会使用Microsoft的{ostress.exe工具(http://sqlmag.com/t-sql/2-tools-keep-sql-server-tuned)和/或以下方法

[3.1]在SSMS中打开一个新查询([New query])窗口并执行以下代码

BEGIN TRAN
    -- Replace 1234 with a new DocumentId
    INSERT INTO dbo.DocumentJob (DocumentId, Status) VALUES (1234, 'New')
-- COMMIT

[3.2]在SSMS中打开另一个查询([新查询])窗口并执行以下代码

BEGIN TRAN
    -- Use the same DocumentId
    INSERT INTO dbo.DocumentJob (DocumentId, Status) VALUES (1234, 'New')
COMMIT

[3.3]第一个INSERT将成功,第二个将失败。

注意:如果您有任何疑问(关于此答案),请询问。 注2:不要忘记COMMIT(或ROLLBACK第一个Tx)。