只有在它不存在的情况下才插入一行

Ada*_*dam 68 sql t-sql sql-server concurrency locking

我总是使用类似下面的东西来实现它:

INSERT INTO TheTable
SELECT
    @primaryKey,
    @value1,
    @value2
WHERE
    NOT EXISTS
    (SELECT
        NULL
    FROM
        TheTable
    WHERE
        PrimaryKey = @primaryKey)
Run Code Online (Sandbox Code Playgroud)

...但是一旦加载,就会发生主键违规.这是唯一插入此表的语句.那么这是否意味着上述陈述不是原子的?

问题是,这几乎不可能随意重建.

也许我可以将其更改为以下内容:

INSERT INTO TheTable
WITH
    (HOLDLOCK,
    UPDLOCK,
    ROWLOCK)
SELECT
    @primaryKey,
    @value1,
    @value2
WHERE
    NOT EXISTS
    (SELECT
        NULL
    FROM
        TheTable
    WITH
        (HOLDLOCK,
        UPDLOCK,
        ROWLOCK)
    WHERE
        PrimaryKey = @primaryKey)
Run Code Online (Sandbox Code Playgroud)

虽然,也许我使用错误的锁或使用过多的锁定或其他东西.

我在stackoverflow.com上看到了其他问题,其中答案是建议"IF(SELECT COUNT(*)... INSERT"等),但我总是在(可能是不正确的)假设单个SQL语句是原子的.

有没有人有任何想法?

gbn*_*gbn 60

那么"JFDI"模式呢?

BEGIN TRY
   INSERT etc
END TRY
BEGIN CATCH
    IF ERROR_NUMBER() <> 2627
      RAISERROR etc
END CATCH
Run Code Online (Sandbox Code Playgroud)

说真的,这是最快和最没有锁定的并发,特别是在大量时.如果UPDLOCK升级并且整个表被锁定怎么办?

阅读第4课:

第4课:在调整索引之前开发upsert proc时,我首先相信该If Exists(Select…)行会触发任何项目并禁止重复.纳达.在短时间内有数千个重复项,因为相同的项目将在相同的毫秒内达到upsert并且两个事务都会看到不存在并执行插入.经过大量测试后,解决方案是使用唯一索引,捕获错误,然后重试允许事务查看行并执行更新而不是插入.

  • @student 35k tps ="每秒35000笔交易".TRY CATCH通过捕获*唯一约束*违规错误(错误号2627)并忽略它来防止插入重复条目.如果错误是*2627,则CATCH将仅重新抛出错误.此代码段存在问题,因为*唯一索引*违规是错误2601.因此您必须检查这两个代码.此解决方案也仅适用于单行INSERT.如果您尝试从一个表插入另一个表,则需要一个不同的策略. (4认同)

GSe*_*erg 23

我添加了最初不存在的HOLDLOCK.如果没有这个提示,请忽略该版本.

就我而言,这应该足够了:

INSERT INTO TheTable 
SELECT 
    @primaryKey, 
    @value1, 
    @value2 
WHERE 
    NOT EXISTS 
    (SELECT 0
     FROM TheTable WITH (UPDLOCK, HOLDLOCK)
     WHERE PrimaryKey = @primaryKey) 
Run Code Online (Sandbox Code Playgroud)

此外,如果您确实想要更新一行(如果它存在)并插入(如果不存在),您可能会发现这个问题很有用.

  • 当行不存在时,你锁定什么? (2认同)
  • 索引中的相关范围(本例中为主键). (2认同)
  • 测试显示两个"选择"完成.where存在(从foo_testing中选择0 with(updlock),其中id = 4);`,*提供`id = 4`不存在*,不要相互冲突,这意味着我的原始答案实际上是错误的.解决方案是添加`HOLDLOCK`提示.查看编辑的答案.谢谢你让我烦扰:) (2认同)
  • 丹尼尔回答我(非常相似)的问题时,有一个很好的解释为什么需要这种锁定:http://stackoverflow.com/questions/3789287/violation-of-unique-key-constraint-on-insert-where-count -0上-SQL服务器3791506分之200#3791506 (2认同)

Chr*_*ith 17

您可以使用MERGE:

MERGE INTO Target
USING (VALUES (@primaryKey, @value1, @value2)) Source (key, value1, value2)
ON Target.key = Source.key
WHEN MATCHED THEN
    UPDATE SET value1 = Source.value1, value2 = Source.value2
WHEN NOT MATCHED BY TARGET THEN
    INSERT (Name, ReasonType) VALUES (@primaryKey, @value1, @value2)
Run Code Online (Sandbox Code Playgroud)

  • 有关@ EBarr点的更多信息,请参阅[本文](http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx) (7认同)
  • 抱歉,如果没有为合并声明添加保持锁定提示,您将遇到OP担心的确切问题. (4认同)