Sql server 在选择插入过程中是否需要锁定提示?

Sql server 在选择插入过程中是否需要锁定提示?,sql-server,Sql Server,我有一个存储过程,如果行键不存在,它会将一行插入表中。看起来是这样的: create proc EmployeeInsertIfNotExists (@id int, @name varchar(50)) as begin SET XACT_ABORT ON begin transaction if not exists(select * from tbl where id = @id) insert into tbl(id, name)

我有一个存储过程,如果行键不存在,它会将一行插入表中。看起来是这样的:

create proc EmployeeInsertIfNotExists
     (@id int, @name varchar(50)) 
as 
begin
    SET XACT_ABORT ON

    begin transaction

    if not exists(select * from tbl where id = @id)
        insert into tbl(id, name) 
        values(id, name)

    commit transaction
end
这个存储过程实际上只是两个语句,一个select语句和一个可能的insert语句。我在一个事务中处理这两个语句,以便它们之间不会发生任何导致异常的事情。
id
列是主键,因此我希望确保不会重复插入相同的id

我的问题是:这是否足以预防问题?我需要在select语句中添加任何提示吗?如果是,我是否需要
HOLDLOCK、TABLOCKX
?这对我来说是新材料

编辑:建议答案

create proc EmployeeInsertIfNotExists
    (@id int, @name varchar(50)) 
as 
begin
    SET XACT_ABORT ON
    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE

    begin transaction

    if not exists(select * from tbl where id = @id)
        insert into tbl(id, name) 
        values(id, name)

    commit transaction
end

您希望将事务隔离级别标记为
serializable
。否则,有人可以在事务的中途插入具有相同ID的行。这被称为“幻影行”


你不需要把整张桌子都锁上。通过使用正确的隔离级别,SQL Server可以更智能地应用其锁。

那么我是否需要将我的过程更改为粘贴的第二个过程?我可以使用merge`语句完全避免事务吗?是的,您的第二个版本应该可以工作。不,合并不是原子的。它与您的原始版本有相同的问题。这对于您正在尝试的操作来说是一种过分的杀伤力。Serializable将损害(杀死?)您机器的性能。如果id是唯一的,则可以保证其他人不能在该列中插入超过一个键值。您可以从进程中获得一个返回值,该值指示插入失败,您可以使用另一个键值重试。
Serializable
是否会实际影响性能取决于许多因素,包括索引方案、写入争用量、SQL Server选择使用的锁类型、,同样,错误处理和重试逻辑可能更便宜或更昂贵,这取决于这两个因素、需要重试的可能性以及是否会发生死锁。但无论如何,两者都比使用表锁好得多。