以线程安全的方式插入具有公共标识符的行

问题描述 投票:0回答:1

我创建了一个动态创建 SQL 代码的应用程序。
该应用程序应该对一些由条形码识别的生产订单进行分组,并将其合并为一个提货订单。

为了实现这一点,我需要写入 SQL Server 数据库:

  1. 通用标识符
  2. 各对应条形码
  3. 要生产的物品数量

通常我会在 SQL Server 中创建一个标识列,然后使用

select scope_identity()
查询新插入的标识,但我需要多次使用我的标识符。

这是我的应用程序当前生成的 SQL 代码:

set xact_abort on 
BEGIN TRAN 

declare @MultiOrderReference as integer
set @MultiOrderReference = (select max(Reference) from PickReference ) + 1
insert into PickReference (Reference, Barcode, Amount) values (@MultiOrderReference, '0068518000', 1)
insert into PickReference (Reference, Barcode, Amount) values (@MultiOrderReference, '0068548000', 4)
insert into PickReference (Reference, Barcode, Amount) values (@MultiOrderReference, '0068550000', 8)

select @MultiOrderReference as Reference
commit tran

这将插入具有公共标识符的行,然后将标识符返回给应用程序。

这在大多数情况下都有效,考虑到这些都是非常小的事务(快),它可能总是有效,但据我了解,它不是线程安全的。因此,如果另一个用户同时运行该应用程序,我想理论上两个应用程序实例可以使用相同的公共标识符。
有办法防止这种情况吗?或者我错了,这实际上是线程安全的,因为我使用的是“begin tran -> commit tran”?

sql sql-server thread-safety
1个回答
2
投票

看起来你应该只使用

SEQUENCE

CREATE SEQUENCE dbo.PickId AS int
  START WITH 1;

然后获取它的值。

SET XACT_ABORT, NOCOUNT ON;
BEGIN TRAN;

DECLARE @MultiOrderReference int = NEXT VALUE FOR dbo.PickId;

INSERT INTO PickReference (Reference, Barcode, Amount)
VALUES
(@MultiOrderReference, '0068518000', 1),
(@MultiOrderReference, '0068548000', 4),
(@MultiOrderReference, '0068550000', 8);

COMMIT;

SELECT @MultiOrderReference AS Reference;

如果您真的想坚持使用现有代码,那么您需要添加锁定提示:

SERIALIZABLE
UPDLOCK
。您还应该在
Reference
上添加索引。

set @MultiOrderReference = isnull((
    select max(Reference)
    from PickReference with (serializable, updlock)
), 0) + 1;
© www.soinside.com 2019 - 2024. All rights reserved.