Mover filas de una mesa a otra

9

Estoy moviendo registros de una base de datos a otra, como parte del proceso de archivo. Quiero copiar las filas a la tabla de destino y luego eliminar las mismas filas de la tabla de origen.

Mi pregunta es, ¿cuál es la forma más eficiente de verificar si la primera inserción fue exitosa antes de eliminar las filas?

Mi idea es esta, pero siento que hay una mejor manera:

@num_records=select count(ID) from Source_Table where (criteria for eligible rows)

insert * into Destination_Table where (criteria for eligible rows)

if ((select count(ID) from Destination_Table where (criteria) )=@numrecords)

delete * from Source_Table where (criteria)

¿Es mejor / posible combinarlo con la función RAISERROR? ¡Gracias!

Dina
fuente

Respuestas:

13

Recomendaría la sintaxis TRY / CATCH junto con transacciones explícitas. Mi suposición para esta solución es que la razón de la falla de inserción es algún tipo de error de SQL atrapable (como una violación de clave, error de conversión / desajuste de tipo de datos, etc.). La estructura se vería así:

BEGIN TRAN

BEGIN TRY
  INSERT INTO foo(col_a,col_b,col_c,recdate)
  SELECT col_a,col_b,col_c,recdate
  FROM bar
  WHERE recdate BETWEEN @startdate AND @enddate

  DELETE FROM bar
  WHERE recdate BETWEEN @startdate AND @enddate

  COMMIT TRAN
END TRY
BEGIN CATCH
  ROLLBACK TRAN
END CATCH

La forma en que funciona esta estructura, si se produce algún error en INSERT o DELETE, toda la acción se revierte. Esto garantiza que toda la acción debe ser exitosa para completarse. Si considera que es necesario, puede combinarlo con THROW para 2012 o RAISERROR en 2008 y anteriores para agregar lógica adicional y forzar una reversión si no se cumple esa lógica.

Otra opción es mirar SET XACT_ABORT ON , aunque creo que la sintaxis TRY / CATCH le brinda más granularidad.

Mike Fal
fuente
19

Si su tabla de archivo no lo hace .

  • Habilita los activadores definidos en él.
  • Participe a ambos lados de una restricción de CLAVE EXTRANJERA.
  • Tener restricciones CHECK o reglas habilitadas.

También podría hacerlo en un solo estado.

DELETE FROM source_table
OUTPUT deleted.Foo,
       deleted.Bar,
       SYSUTCDATETIME()
INTO archive_table(Foo, Bar, archived)
WHERE  Foo = 1; 

Esto tendrá éxito o fallará como unidad y también evitará posibles condiciones de carrera con filas agregadas entre INSERTel archivo y DELETE(aunque su WHEREcláusula bien puede hacer que esto sea extremadamente improbable de todos modos).

Martin Smith
fuente
Ninguna de las anteriores. Supongo que podría ir por ese camino, me gusta el minimalismo de código. Simplemente no quiero perder registros si la inserción falla por algún motivo. (es decir, bloqueo de la mesa, tiempos de espera, etc.) ¡Gracias!
Dina
@Dina - ¿Indicabas que es posible con la OUTPUTcláusula? No es porque es todo una declaración. También evita el problema de tener que leer las filas dos veces (y posiblemente perder las filas que se agregaron entre la lectura para la inserción y la lectura para la eliminación)
Martin Smith
Sí, eso es lo que quise decir. Gracias, entiendo tu punto.
Dina
FWIW: este método provocará un crecimiento del archivo de registro cercano al del tamaño de la tabla original. Asegúrate de poder vivir con eso. Si no puede, divídalo en lotes con DELETE TOP (N) y un ciclo While que verifica la variable @@ rowcount.
Wjdavis5
1

La forma en que he pensado en archivar (que estoy seguro tampoco es perfecto), es agregar una columna de bits a la nueva tabla de archivo como 'Archivado' que tendría el valor de 1 después de la transferencia exitosa de un registro. Y una vez que transfiere todos los registros, puede realizar una operación de eliminación mientras busca este valor de campo 'Archivado' de '1', es decir, Verdadero de la tabla archivada.

Y estoy de acuerdo con Mike en usar Try / Catch.

avakharia
fuente
1

Prueba esto:

INSERT dbo.newtable(
      name,
      department,
      Salary
) SELECT 
            name,
            FirstName,
            Lastname
      FROM    (
           DELETE dbo.oldtable
           OUTPUT
                   DELETED.name,
                   DELETED.department,
                   DELETED.Salary
           WHERE ID  IN ( 1001, 1003, 1005 )
      ) AS RowsToMove;

SELECT * FROM dbo.newtable;
SELECT * FROM dbo.oldtable;
Nadeem
fuente