如果EXISTS,那么选择ELSE INSERT然后选择

Phi*_*enn 49 sql t-sql sql-server sql-server-2005

How do you say以下Microsoft SQL Server 2005:

IF EXISTS (SELECT * FROM Table WHERE FieldValue='') THEN
   SELECT TableID FROM Table WHERE FieldValue=''
ELSE
   INSERT INTO TABLE(FieldValue) VALUES('')
   SELECT TableID FROM Table WHERE TableID=SCOPE_IDENTITY()
END IF
Run Code Online (Sandbox Code Playgroud)

我要做的是查看是否已经有一个空白的fieldvalue,如果有则返回TableID,否则插入一个空白的fieldvalue并返回相应的主键.

zvo*_*kov 60

您需要在事务中执行此操作以确保两个并发客户端不会两次插入相同的fieldValue:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION
    DECLARE @id AS INT
    SELECT @id = tableId FROM table WHERE fieldValue=@newValue
    IF @id IS NULL
    BEGIN
       INSERT INTO table (fieldValue) VALUES (@newValue)
       SELECT @id = SCOPE_IDENTITY()
    END
    SELECT @id
COMMIT TRANSACTION
Run Code Online (Sandbox Code Playgroud)

您还可以使用双重检查锁定来减少锁定开销

DECLARE @id AS INT
SELECT @id = tableID FROM table (NOLOCK) WHERE fieldValue=@newValue
IF @id IS NULL
BEGIN
    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
    BEGIN TRANSACTION
        SELECT @id = tableID FROM table WHERE fieldValue=@newValue
        IF @id IS NULL
        BEGIN
           INSERT INTO table (fieldValue) VALUES (@newValue)
           SELECT @id = SCOPE_IDENTITY()
        END
    COMMIT TRANSACTION
END
SELECT @id
Run Code Online (Sandbox Code Playgroud)

至于为什么需要ISOLATION LEVEL SERIALIZABLE,当你在一个可序列化的事务中时,第一个命中表的SELECT会创建一个覆盖记录所在位置的范围锁,因此在此事务结束之前没有其他人可以插入相同的记录.

如果没有ISOLATION LEVEL SERIALIZABLE,默认隔离级别(READ COMMITTED)将不会在读取时锁定表,因此在SELECT和UPDATE之间,某人仍然可以插入.具有READ COMMITTED隔离级别的事务不会导致SELECT锁定.使用REPEATABLE READS的事务会锁定记录(如果找到)而不是间隙.

  • +1我不明白为什么只考虑种族条件和并发性的**答案在零票数上萎缩. (6认同)
  • @binki,当在一个可序列化的事务中,第一个命中表的SELECT时,会创建一个覆盖记录所在位置的*范围锁*,因此在此事务结束之前,没有其他人可以插入相同的记录.如果没有ISOLATION LEVEL SERIALIZABLE,默认隔离级别(READ COMMITTED)将不会在读取时锁定表,因此在select和update之间,某人仍然可以插入.具有READ COMMITTED隔离级别的事务,不会导致SELECT锁定.使用REPEATABLE READS的事务,锁定记录(如果找到)但不是间隙. (5认同)
  • 您是否可以扩展解释为什么"ISOLATION LEVEL SERIALIZABLE"是必要的,如果您没有设置它会发生什么? (2认同)

Jan*_*ane 32

IF EXISTS (SELECT 1 FROM Table WHERE FieldValue='') 
BEGIN
   SELECT TableID FROM Table WHERE FieldValue=''
END
ELSE
BEGIN
   INSERT INTO TABLE(FieldValue) VALUES('')
   SELECT SCOPE_IDENTITY() AS TableID
END
Run Code Online (Sandbox Code Playgroud)

有关IF ELSE的更多信息,请参见此处

注意:编写没有SQL Server安装方便双重检查,但我认为这是正确的

另外,我已经将EXISTS位更改为SELECT 1而不是SELECT*,因为您不关心EXISTS中返回的内容,只要有什么东西我也改变了SCOPE_IDENTITY()位以返回标识假设TableID是标识列

  • 我更喜欢在我的代码中避免使用SELECT* - 它不是一个习惯进入的好习惯,因此我通常在执行存在时执行SELECT 1 (4认同)
  • 'SELECT 1'并不重要.你是在改变它只是为了指出你不关心细节吗?它没有帮助表现. (2认同)

Dav*_*vid 7

你很亲密:

IF EXISTS (SELECT * FROM Table WHERE FieldValue='')
   SELECT TableID FROM Table WHERE FieldValue=''
ELSE
BEGIN
   INSERT INTO TABLE (FieldValue) VALUES ('')
   SELECT TableID FROM Table WHERE TableID=SCOPE_IDENTITY()
END
Run Code Online (Sandbox Code Playgroud)