在SQL Server中写缓冲

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

我有一个在SQL Server 2008上运行的程序。有一个创建新货件的功能。创建货件所需的最小数据是idno,subno和class。主键是idno + idsub。我创建了一个存储过程来创建一个新的货件,如下所示

ALTER Proc [dbo].[spDP_CreateNewShipment](@Class char(10),@idno int = 0, @idsub smallint = 1)
as

If @idno = 0
Begin
Select @idno = Max(idno)+1 From Shipment
End 
Insert Into Shipment
(idno,idsub, class)
Values (@idno,@idsub,@class)

如果我将@idno传递给0,它应该创建一个包含下一个可用idno的货件。这很有效,除非有时两个新货将获得相同的新idno。似乎时间必须完全相同,这应该几乎不可能,但它发生了。我能想到的另一个可能性是Insert可能会被缓冲,可能不会立即发生。我对SQL Server设置知之甚少。有没有人知道可能导致写入不立即发生的设置?

sql sql-server tsql
3个回答
1
投票

将您的语句包装到transaction并设置最高隔离级别。像这样的东西。

ALTER Proc [dbo].[spDP_CreateNewShipment](@Class char(10),@idno int = 0, @idsub smallint = 1)
as
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION
If @idno = 0
Begin
Select @idno = Max(idno)+1 From Shipment
End 
Insert Into Shipment
(idno,idsub, class)
Values (@idno,@idsub,@class)
COMMIT TRANSACTION

2
投票

简短的回答是:在这种情况下你不需要它,而是使用identity主键。

你因为race condition而遇到了这个问题。在生成@idno之后,另一个线程也会生成相同的@idno。然后两个线程都试图插入具有相同@idno的行,因此第二个插入违反了primary key唯一性规则。

Identity主键允许您只插入非Id数据列并将next-id-generation留给数据库。然后你可以问它是什么产生的。

对于insert代码示例,请查看此SO answer。像这样的东西:

if @idno = 0 begin
    Insert Into Shipment(idsub, class)
    Output inserted.idno   -- it outputs to the client idno of the inserted row
    Values (@idno,@idsub,@class)
end
else begin
    -- @idno != 0 case
    -- are you SURE you wanna insert with @idno generated on client?

    -- if you HAVE TO, then you probably should use 
    -- GUID (aka uniqueidentifier) for idno column
    -- instead of identity int
end

有关identity列的更多信息,请查看msdn。简而言之,您声明此列的下一个值是基于一些初始值seedstep生成的。最常见的(seed, step)(1, 1),但你可以设置你想要的任何东西。


0
投票

在事务中执行操作可能更好;

begin transaction
    If @idno = 0
    Begin
        Select @idno = Max(idno)+1 From Shipment
    End 
    Insert Into Shipment (idno,idsub, class) Values (@idno,@idsub,@class)
commit transaction
© www.soinside.com 2019 - 2024. All rights reserved.