2010-10-25 18 views

Respuesta

-1

Consulte la pregunta this si desea utilizar el comando UPSERT/MERGE en Oracle. De lo contrario, solo resuelva su problema en el lado del cliente haciendo primero un count(1) y luego decidir si insertar o actualizar.

+1

No se podía ser menos correcta; Oracle ha respaldado la instrucción MERGE desde Oracle 9i. –

+1

La x-ref es buena; sin embargo, contradice lo que dices. No es mi -1, pero al menos simpatizo con eso. –

2

La manera en que siempre lo hace (suponiendo que los datos nunca debe ser eliminado, sólo se ha insertado) es

  • En primer lugar hacer una insert, si esto falla con una violación de restricción único, entonces usted sabe la fila es allí,
  • Luego hacen un update

Desafortunadamente muchos marcos como Hibernate tratan a todos los errores de base de datos (por ejemplo, única violación de restricción) como condiciones irrecuperables, lo que no siempre es fácil. (En Hibernate la solución es abrir una nueva sesión/transacción sólo para ejecutar este comando insert.)

No se puede hacer un select count(*) .. where .. ya que incluso si eso devuelve cero, y por lo tanto usted elige hacer una insert, entre el momento en que realice el select y el insert, alguien más podría haber editado la fila insert y, por lo tanto, su insert fallará.

+0

Incluso con la restricción insertar solo, el uso de dos transacciones podría dar lugar a excepciones de exclusividad si hay múltiples actualizadores escribiendo en la tabla. –

+0

David Mann, siento no haberlo entendido; Por favor aclara –

+0

Hola Adrian, estoy pensando en un caso en el que la declaración UPDATE solo es válida si los datos no han cambiado desde el intento de INSERTAR, y hay varios procesos que realizan las inserciones y actualizaciones. A menos que todas las inserciones y actualizaciones de la tabla estén sincronizadas, a veces la ACTUALIZACIÓN puede tener éxito y, a veces, puede fallar dependiendo de cómo se intercalen los procesos. Me pareció una especie de caso de esquina hasta que me pidieron que realizara ACTUALIZACIONES que solo son válidas si el estado de los datos desde la última lectura sigue siendo el mismo. –

51

MERGE no necesita "varias tablas", pero sí necesita una consulta como fuente. Algo como esto debería funcionar:

MERGE INTO mytable d 
USING (SELECT 1 id, 'x' name from dual) s 
ON (d.id = s.id) 
WHEN MATCHED THEN UPDATE SET d.name = s.name 
WHEN NOT MATCHED THEN INSERT (id, name) VALUES (s.id, s.name); 

Alternativamente, usted puede hacer esto en PL/SQL:

BEGIN 
    INSERT INTO mytable (id, name) VALUES (1, 'x'); 
EXCEPTION 
    WHEN DUP_VAL_ON_INDEX THEN 
    UPDATE mytable 
    SET name = 'x' 
    WHERE id = 1; 
END; 
+1

+1 No sé acerca de la instrucción 'MERGE', pero en cuanto a la excepción' DUP_VAL_ON_INDEX' manejo, esa es definitivamente una buena solución, sabiendo que el manejo de excepciones de Oracle se utiliza regularmente para dicho comportamiento. =) –

+1

+1; se debe tener en cuenta que la solución alternativa generalmente es mucho menos eficiente. – DCookie

2

usted podría utilizar la variable SQL%ROWCOUNT Oracle:

UPDATE table1 
    SET field2 = value2, 
     field3 = value3 
WHERE field1 = value1; 

IF (SQL%ROWCOUNT = 0) THEN 

    INSERT INTO table (field1, field2, field3) 
    VALUES (value1, value2, value3); 

END IF; 

Sería más fácil sólo para determinar si su clave principal (es decir, field1) tiene un valor y luego realizar una inserción o actualización en consecuencia. Es decir, si usa dichos valores como parámetros para un procedimiento almacenado.

+8

Si tiene varias sesiones escribiendo simultáneamente, puede encontrarse con la situación en que 'update' toca cero filas, por lo que supone que no hay fila y necesita hacer un 'insert', pero mientras tanto alguien ha hecho una' inserte' para que su 'inserción' falle con una violación de restricción única. Es por eso que es importante hacer 'insert' (y capturar violaciones de restricciones únicas) luego' update', y no al revés. –

4
merge into MY_TABLE tgt 
using (select [expressions] 
     from dual) src 
    on (src.key_condition = tgt.key_condition) 
when matched then 
    update tgt 
     set tgt.column1 = src.column1 [,...] 
when not matched then 
    insert into tgt 
     ([list of columns]) 
    values 
     (src.column1 [,...]); 
0

HC vías :)

DECLARE 
    rt_mytable mytable%ROWTYPE; 
    CURSOR update_mytable_cursor(p_rt_mytable IN mytable%ROWTYPE) IS 
    SELECT * 
    FROM mytable 
    WHERE ID = p_rt_mytable.ID 
    FOR UPDATE; 
BEGIN 
    rt_mytable.ID := 1; 
    rt_mytable.NAME := 'x'; 
    INSERT INTO mytable VALUES (rt_mytable); 
EXCEPTION WHEN DUP_VAL_ON_INDEX THEN 
    <<update_mytable>> 
    FOR i IN update_mytable_cursor(rt_mytable) LOOP 
    UPDATE mytable SET  
     NAME = p_rt_mytable.NAME 
    WHERE CURRENT OF update_mytable_cursor; 
    END LOOP update_mytable; 
END; 
+0

Nada especial sobre esto porque el principio básico de esto ya se publicó anteriormente. Simplemente por diversión :) –

+0

¿Qué significa "HC"? –

+0

Significa hardcore –

Cuestiones relacionadas