确保单个添加到 SQL table
Ensuring single add into SQL table
我有一个 DocumentJob table,其中包含要针对给定文档执行的作业。 table 中的每一行都有 jobId、documentId 和 jobstatus。
多个线程/进程将在任何给定时间尝试添加到此 table(使用下面给出的代码)
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 - 我只想在 运行 或新文档没有其他作业的情况下添加新作业。以下代码片段是否满足上述要求,或者在某些情况下是否会违反上述条件?
我的objective是了解当同时调用上述过程时table是否会被正确锁定等。
[1] 我要做的第一件事 是创建一个唯一的过滤索引:
CREATE UNIQUE NONCLUSTERED INDEX IUF_DocumentJob_DocumentId
ON dbo.DocumentJob (DocumentId)
--INCLUDE (...)
WHERE Status in ('Running', 'New')
此唯一索引保证 dbo.DocumentJob
table 将拥有一个具有以下状态之一的唯一 DocumentId
:'Running'
或 'New'
。
当状态为 'Running'
或 'New'
.
时,这足以防止重复 dbo.DocumentJob
s
[2] 在 [1] 之后,当前问题中包含的源代码可以替换为简单的
INSERT INTO dbo.DocumentJob (DocumentId, Status) VALUES (@DocumentId, 'New')
对于任何{@DocumentId
值和Running
或New
状态}只有第一个INSERT
(或UPDATE
)执行会成功,下一个执行将失败。
注意:我会将此代码封装在事务中(参见 SET XACT_ABORT ON
),也会封装在 TRY ... CATCH
块中(此主题未在本答案中介绍)。
[3] 为了进行测试,我将使用 Microsoft (http://sqlmag.com/t-sql/2-tools-keep-sql-server-tuned) 的 ostress.exe 工具和/或以下方法
[3.1] 在 SSMS 中打开一个新查询([新查询])window 并执行以下代码
BEGIN TRAN
-- Replace 1234 with a new DocumentId
INSERT INTO dbo.DocumentJob (DocumentId, Status) VALUES (1234, 'New')
-- COMMIT
[3.2] 在 SSMS 中打开另一个查询([新查询])window 并执行以下代码
BEGIN TRAN
-- Use the same DocumentId
INSERT INTO dbo.DocumentJob (DocumentId, Status) VALUES (1234, 'New')
COMMIT
[3.3]第一个INSERT
会成功,第二个会失败。
注意:如果您有任何问题(关于这个答案)请提问。
注意 2:不要忘记提交(或先回滚 Tx)。
我有一个 DocumentJob table,其中包含要针对给定文档执行的作业。 table 中的每一行都有 jobId、documentId 和 jobstatus。
多个线程/进程将在任何给定时间尝试添加到此 table(使用下面给出的代码)
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 - 我只想在 运行 或新文档没有其他作业的情况下添加新作业。以下代码片段是否满足上述要求,或者在某些情况下是否会违反上述条件?
我的objective是了解当同时调用上述过程时table是否会被正确锁定等。
[1] 我要做的第一件事 是创建一个唯一的过滤索引:
CREATE UNIQUE NONCLUSTERED INDEX IUF_DocumentJob_DocumentId
ON dbo.DocumentJob (DocumentId)
--INCLUDE (...)
WHERE Status in ('Running', 'New')
此唯一索引保证 dbo.DocumentJob
table 将拥有一个具有以下状态之一的唯一 DocumentId
:'Running'
或 'New'
。
当状态为 'Running'
或 'New'
.
dbo.DocumentJob
s
[2] 在 [1] 之后,当前问题中包含的源代码可以替换为简单的
INSERT INTO dbo.DocumentJob (DocumentId, Status) VALUES (@DocumentId, 'New')
对于任何{@DocumentId
值和Running
或New
状态}只有第一个INSERT
(或UPDATE
)执行会成功,下一个执行将失败。
注意:我会将此代码封装在事务中(参见 SET XACT_ABORT ON
),也会封装在 TRY ... CATCH
块中(此主题未在本答案中介绍)。
[3] 为了进行测试,我将使用 Microsoft (http://sqlmag.com/t-sql/2-tools-keep-sql-server-tuned) 的 ostress.exe 工具和/或以下方法
[3.1] 在 SSMS 中打开一个新查询([新查询])window 并执行以下代码
BEGIN TRAN
-- Replace 1234 with a new DocumentId
INSERT INTO dbo.DocumentJob (DocumentId, Status) VALUES (1234, 'New')
-- COMMIT
[3.2] 在 SSMS 中打开另一个查询([新查询])window 并执行以下代码
BEGIN TRAN
-- Use the same DocumentId
INSERT INTO dbo.DocumentJob (DocumentId, Status) VALUES (1234, 'New')
COMMIT
[3.3]第一个INSERT
会成功,第二个会失败。
注意:如果您有任何问题(关于这个答案)请提问。 注意 2:不要忘记提交(或先回滚 Tx)。