2012-03-26 4 views
13

MERGEステートメントをUPSERTとして使用して、新しいレコードを追加するか、現在のレコードを更新します。私は複数の接続と複数のステートメント(スレッドごとに1つの接続とステートメント)を介してデータベースを駆動する複数のスレッドを持っています。私は一度に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

+0

@Paolo 'ALTER TABLE CustomerSpend ADD CONSTRAINT [PK_CustomerSpend] PRIMARY KEY CLUSTERED(ID)'。より良い方法がありますか? – OldCurmudgeon

+0

申し訳ありませんが、私はあなたがクエリで渡していたIDの実際の値を意味しました。 Mikaelさんが下にあります。トランザクションはアトミックですが、同じキーを挿入しようとしている複数のスレッドを停止するものはありません。 – Paolo

答えて

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

+0

「*並行性が高い場合*」の意味はいつ教えていただけますか? – Pankaj

+1

@abcdefghi [マージステートメントの同時実行](http://en.wikipedia.org/wiki/Concurrency_(computer_science))。 –

関連する問題