我有一个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 - 我想仅在没有正在运行或新的文档的其他作业时添加新作业。以下代码段是否会处理上述要求,或者是否存在可能违反上述条件的情况?
我的目标是在同时调用上述过程时理解表是否会被正确锁定等。
答案 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
值以及Running
或New
状态},只有第一个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)。