在SQL Server中写缓冲

时间:2017-12-20 18:23:25

标签: sql sql-server tsql

我有一个在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设置知之甚少。有没有人知道可能导致写入不立即发生的设置?

3 个答案:

答案 0 :(得分:2)

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

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

Identity主键允许您只插入非Id-data-columns并将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。简而言之,您声明此列的下一个值是基于建议seed的某个初始值step生成的。最常见的(seed, step)(1, 1),但您可以设置任何您想要的内容。

答案 1 :(得分: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 :(得分: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