Pregunta

He escrito un procedimiento almacenado que se va a hacer una actualización si existe un registro, de lo contrario es hacer una inserción.Se ve algo como esto:

update myTable set Col1=@col1, Col2=@col2 where ID=@ID
if @@rowcount = 0
insert into myTable (Col1, Col2) values (@col1, @col2)

Mi lógica detrás de lo escrito en esta forma es que la actualización se realice de manera implícita seleccione el uso de la cláusula where y si devuelve 0, a continuación, la inserción se llevará a cabo.

La alternativa a esta forma sería hacer un select y, a continuación, basado en el número de filas devueltas hacer un update o insert.Esto me considera ineficiente porque si usted va a hacer una actualización que hará que 2 selecciona (la primera explícita seleccione la llamada y el segundo implícito en la ubicación de la actualización).Si el proc fueron a hacer un insert, entonces no habría ninguna diferencia en la eficiencia.

Es mi lógica de sonido aquí?¿Es esta la forma que se combinan en un insert y update en un procedimiento almacenado?

¿Fue útil?

Solución

Su suposición es correcta, esta es la forma óptima de hacerlo y se llama upsert/merge.

Importancia de UPSERT - de sqlservercentral.com:

Para cada actualización en el caso de los mencionados anteriormente nos están quitando uno adicional de la lectura de la tabla si nos el uso de la UPSERT en lugar de que EXISTE.Por desgracia para la Inserción, tanto en el UPSERT y SI EXISTE uso de los métodos de la mismo número de lecturas sobre la mesa.Por lo tanto, la comprobación de la existencia sólo se debe hacer cuando hay un razón muy válida para justificar la adicional de I/O.La optimización de la forma de hacer las cosas es para asegurarse de que usted tienen poco que se lee como sea posible en el DB.

La mejor estrategia es tratar el la actualización.Si no hay filas están afectados por la actualización, a continuación, inserte.En la mayoría de los circunstancias, la fila ya existen y sólo uno de e/S será necesario.

Editar:Por favor, echa un vistazo esta respuesta y los enlaces de blog para aprender acerca de los problemas con este patrón y cómo hacer que funcione seguro.

Otros consejos

Por favor, lea la post en mi blog para un buen seguro de patrón que usted puede usar.Hay un montón de consideraciones, y la aceptó respuesta a esta pregunta está lejos de ser seguro.

Para una respuesta rápida tratar el siguiente patrón.Funcionará bien en SQL 2000 y anteriores.SQL 2005 le da el manejo de errores que abre otras opciones y SQL 2008 ofrece una COMBINACIÓN de comandos.

begin tran
   update t with (serializable)
   set hitCount = hitCount + 1
   where pk = @id
   if @@rowcount = 0
   begin
      insert t (pk, hitCount)
      values (@id,1)
   end
commit tran

Si se va a utilizar con SQL Server 2000/2005 el código original debe estar encerrada en la transacción para asegurarse de que los datos siguen siendo constantes en simultáneo escenario.

BEGIN TRANSACTION Upsert
update myTable set Col1=@col1, Col2=@col2 where ID=@ID
if @@rowcount = 0
insert into myTable (Col1, Col2) values (@col1, @col2)
COMMIT TRANSACTION Upsert

Esto tendrá un rendimiento adicional de costo, pero se asegurará de que la integridad de los datos.

Añadir, como ya se ha sugerido, de COMBINACIÓN debe utilizarse cuando estén disponibles.

MERGE es una de las nuevas características de SQL Server 2008, por el camino.

No sólo se necesita ejecutar en la transacción, también necesita un alto nivel de aislamiento.Yo de hecho por defecto el nivel de aislamiento Read Comprometidos y este código necesita Serializable.

SET transaction isolation level SERIALIZABLE
BEGIN TRANSACTION Upsert
UPDATE myTable set Col1=@col1, Col2=@col2 where ID=@ID
if @@rowcount = 0
  begin
    INSERT into myTable (ID, Col1, Col2) values (@ID @col1, @col2)
  end
COMMIT TRANSACTION Upsert

Tal vez también añadir @@error de verificación y reversión podría ser una buena idea.

Si usted no está haciendo una mezcla en SQL 2008 debe cambiar a:

si @@rowcount = 0 y @@error=0

de lo contrario, si la actualización falla por alguna razón, a continuación, se va a tratar y a una inserción posterior debido a que el recuento de filas en un error en la declaración es 0

Gran fan de la UPSERT, realmente reduce el código para administrar.Aquí hay otra manera de hacerlo:Uno de los parámetros de entrada es de IDENTIFICACIÓN, si la ID es NULL o 0, usted sabe que es una INSERCIÓN, de lo contrario es una actualización.Supone que la aplicación sabe si hay un ID, por lo que no funcionan en todas las situaciones, pero se corte la ejecuta la mitad en caso de hacer.

Su lógica parece razonable, pero es posible que desee considerar la adición de un poco de código para evitar la inserción si había pasado en un determinado clave principal.

De lo contrario, si siempre estás haciendo una inserción si la actualización no afecta a todos los registros, lo que sucede cuando alguien borra el registro antes de "UPSERT" se ejecuta?Ahora el récord que estaba tratando de actualizar no existe, así que voy a crear un registro en su lugar.Que probablemente no es el comportamiento que usted estaba buscando.

Modificado Dima Malenko post:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE 

BEGIN TRANSACTION UPSERT 

UPDATE MYTABLE 
SET    COL1 = @col1, 
       COL2 = @col2 
WHERE  ID = @ID 

IF @@rowcount = 0 
  BEGIN 
      INSERT INTO MYTABLE 
                  (ID, 
                   COL1, 
                   COL2) 
      VALUES      (@ID, 
                   @col1, 
                   @col2) 
  END 

IF @@Error > 0 
  BEGIN 
      INSERT INTO MYERRORTABLE 
                  (ID, 
                   COL1, 
                   COL2) 
      VALUES      (@ID, 
                   @col1, 
                   @col2) 
  END 

COMMIT TRANSACTION UPSERT 

Usted puede capturar el error y enviar el expediente a un error de insertar tabla.
Necesitaba hacer esto porque estamos tomando los datos que se envían a través de WSDL y si es posible arreglar internamente.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top