MERGE 是 SQL 2008 中的原子语句吗?

Posted

技术标签:

【中文标题】MERGE 是 SQL 2008 中的原子语句吗?【英文标题】:Is MERGE an atomic statement in SQL2008? 【发布时间】:2012-03-26 11:58:10 【问题描述】:

我使用MERGE 语句作为UPSERT 来添加新记录或更新当前记录。我有多个线程通过多个连接和多个语句(每个线程一个连接和一个语句)驱动数据库。我一次批处理 50 个语句。

在我的测试中发现duplicate key 违规令我感到非常惊讶。我预计这是不可能的,因为MERGE 将作为单个事务执行,不是吗?

我的 Java 代码如下所示:

private void addBatch(Columns columns) throws SQLException 
  try 
    // Set parameters.
    for (int i = 0; i < columns.size(); i++) 
      Column c = columns.get(i);
      // Column type is an `enum` with a `set` method appropriate to its type, e.g. setLong, setString etc.
      c.getColumnType().set(statement, i + 1, c.getValue());
    
    // Add the insert as a batch.
    statement.addBatch();
    // Ready to execute?
    if (++batched >= MaxBatched) 
      statement.executeBatch();
      batched = 0;
    
   catch (SQLException e) 
    log.warning("addBatch failed " + sql + " thread " + Thread.currentThread().getName(), e);
    throw e;
  

查询如下所示:

MERGE INTO CustomerSpend AS T 
USING ( SELECT ? AS ID, ? AS NetValue, ? AS VoidValue ) AS V 
ON T.ID = V.ID 
WHEN MATCHED THEN 
    UPDATE SET T.ID = V.ID, T.NetValue = T.NetValue + V.NetValue, T.VoidValue = T.VoidValue + V.VoidValue 
WHEN NOT MATCHED THEN 
    INSERT ( ID,NetValue,VoidValue ) VALUES ( V.ID, V.NetValue, V.VoidValue );

错误显示:

java.sql.BatchUpdateException: Violation of PRIMARY KEY constraint 'PK_CustomerSpend'. Cannot insert duplicate key in object 'dbo.CustomerSpend'. The duplicate key value is (498288              ).
at net.sourceforge.jtds.jdbc.JtdsStatement.executeBatch(JtdsStatement.java:944)
at x.db.Db$BatchedStatement.addBatch(Db.java:299)
...

表上的键是ID 字段上的PRIMARY 键。

【问题讨论】:

如何生成主键(V.ID)? @Paolo ALTER TABLE CustomerSpend ADD CONSTRAINT [PK_CustomerSpend] PRIMARY KEY CLUSTERED (ID)。有没有更好的办法? 抱歉,我的意思是您在查询中传递的 ID 的实际值。 Mikael 在下面得到了它 - 事务是原子的,但没有什么可以阻止多个线程尝试插入相同的密钥 【参考方案1】:

MERGE 是原子的,意味着要么提交所有更改,要么回滚所有更改。

在高并发的情况下不会防止重复键。添加holdlock 提示会解决这个问题。

MERGE INTO CustomerSpend WITH (HOLDLOCK) AS T 
USING ( SELECT ? AS ID, ? AS NetValue, ? AS VoidValue ) AS V 
ON T.ID = V.ID 
WHEN MATCHED THEN 
    UPDATE SET T.ID = V.ID, T.NetValue = T.NetValue + V.NetValue, T.VoidValue = T.VoidValue + V.VoidValue 
WHEN NOT MATCHED THEN 
    INSERT ( ID,NetValue,VoidValue ) VALUES ( V.ID, V.NetValue, V.VoidValue );

【讨论】:

你能告诉我们“in case of high concurrency”是什么时候吗? @abcdefghi Simultaneous executions of the merge statement. 有关并行风险的更多信息:www.mssqltips.com/sqlservertip/3074(以及 T-SQL 中 MERGE几个其他问题)。

以上是关于MERGE 是 SQL 2008 中的原子语句吗?的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server 2008 中的 MERGE 语句

Sql Server 2008 MERGE - 获取计数的最佳方式

在 SQL Server 2008 R2 的 MERGE 语句中更新插入的记录

SQL Server 2008 MERGE 语法中的 USING 是啥?

sql server 2008 merge matched判定条件

SQL2008中Merge的用法