2012-03-26 39 views
13

我使用MERGE語句作爲UPSERT來添加新記錄或更新當前的記錄。我有多個線程通過多個連接和多個語句(每個線程一個連接和語句)來驅動數據庫。我一次批量陳述50。MERGE是SQL2008中的一個原子語句嗎?

我很驚訝在我的測試中得到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) 
... 

在桌子上,關鍵是PRIMARY關鍵在ID字段。

+0

你是如何生成主鍵(V.ID)? – Paolo 2012-03-26 12:02:20

+0

@Paolo'ALTER TABLE CustomerSpend ADD CONSTRAINT [PK_CustomerSpend] PRIMARY KEY CLUSTERED(ID)'。有沒有更好的辦法? – OldCurmudgeon 2012-03-26 12:37:05

+0

對不起,我的意思是您在查詢中傳遞的ID的實際值。 Mikael的下面 - 交易是原子的,但沒有什麼能阻止多個線程試圖插入相同的密鑰 – Paolo 2012-03-26 21:31:00

回答

26

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); 
+0

謝謝!這就說得通了。 – OldCurmudgeon 2012-03-26 12:34:34

+0

你能告訴我們什麼時候「*在高併發性*的情況下」的含義? – Pankaj 2012-08-30 17:14:24

+1

@abcdefghi [同時執行合併聲明](http://en.wikipedia.org/wiki/Concurrency_(computer_science))。 – 2012-08-30 17:19:33