2012-03-26 14 views
13

Estoy usando una declaración MERGE como UPSERT para agregar un nuevo registro o actualizar el actual. Tengo varios hilos que manejan la base de datos a través de múltiples conexiones y múltiples declaraciones (una conexión y una declaración por hilo). Estoy procesando las declaraciones 50 a la vez.¿MERGE es una declaración atómica en SQL2008?

Estaba muy sorprendido de tener una infracción duplicate key durante mis pruebas. Esperaba que fuera imposible porque el MERGE se realizará como una sola transacción, ¿o no?

código

Mi Java se parece a:

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; 
    } 
} 

La consulta es el siguiente:

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

El error lee:

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) 
... 

La clave sobre la mesa es una clave PRIMARY en el campo ID.

+0

¿Cómo está generando la clave principal (V.ID)? – Paolo

+0

@Paolo 'ALTER TABLE CustomerSpend ADD CONSTRAINT [PK_CustomerSpend] PRIMARY KEY CLUSTERED (ID)'. ¿Hay una mejor manera? – OldCurmudgeon

+0

Disculpe, quise decir el valor real de la ID que estaba pasando en la consulta. Mikael lo tiene a continuación: la transacción es atómica, pero no hay nada que impida que varios hilos intenten insertar la misma clave – Paolo

Respuesta

26

MERGE es atómico, lo que significa que se han confirmado todos los cambios o se han revertido todos los cambios.

No evita las claves duplicadas en caso de concurrencia alta. Agregar holdlock sugerencia se encargará de eso.

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

¡Gracias! Eso tiene sentido. – OldCurmudgeon

+0

¿Puede indicarnos por favor el significado de "* en caso de concurrencia alta *"? – Pankaj

+1

@abcdefghi [Ejecuciones simultáneas de la declaración de fusión] (http://en.wikipedia.org/wiki/Concurrency_ (computer_science)). –

Cuestiones relacionadas