2009-06-17 14 views
9

Estoy tratando de insertar una gran cantidad de datos en el servidor SQL. Mi tabla de destino tiene un índice único llamado "Hash".SqlBulkCopy Error al manejar/continuar con el error

Me gustaría reemplazar mi implementación SqlDataAdapter con SqlBulkCopy. En SqlDataAapter hay una propiedad llamada "ContinueUpdateOnError", cuando se establece en true adapter.Update (table) insertará todas las filas posibles y etiquetará las filas de error con la propiedad RowError.

La pregunta es ¿cómo puedo usar SqlBulkCopy para insertar datos tan rápido como sea posible sin perder de vista las filas que se insertaron y las filas que no (debido al índice único)?

Aquí es la información adicional:

  1. El proceso es iterativo, a menudo establecer en un horario a repetir.

  2. Las tablas de origen y de destino pueden ser enormes, a veces millones de filas.

  3. Aunque es posible verificar primero los valores hash, se requieren dos transacciones por fila (primero para seleccionar el hash de la tabla de destino, luego realizar la inserción). Creo que en el caso de adapter.update (table), es más rápido verificar el RowError que verificar los hits por fila.

+1

¿Por qué no eliminar las filas que ya existen del conjunto de datos que se van a insertar, antes de intentar insertar? – ChrisW

+0

ChrisW, aprecia tu sugerencia. Si está sugiriendo que ejecute esta consulta "delete from table where hash in (value1, value2, ..). No puedo usar este enfoque porque tratará cada fila como si no existiera en la tabla de destino, sin embargo, a otras partes de mi sistema solo les gusta procesar los nuevos registros (los que no tienen la colisión hash). – Paladin

+0

@Paladin, consulte la respuesta expandida –

Respuesta

6

SqlBulkCopy, tiene un manejo de errores muy limitado, por defecto ni siquiera comprueba las restricciones.

Sin embargo, es rápido, realmente muy rápido.

Si desea solucionar el problema de la clave duplicada e identificar qué filas son duplicadas en un lote. Una opción es:

  • tran inicio
  • Coge un TABLOCKX en la tabla seleccione todos los valores "Hash" actuales y Chuck en un HashSet.
  • Filtra los duplicados e informa.
  • insertar los datos
  • commit tran

Este proceso funcionará con eficacia si va a insertar enormes conjuntos y el tamaño de los datos iniciales de la tabla no es demasiado grande.

Puede ampliar su pregunta para incluir el resto del contexto del problema.

EDITAR

ahora que tengo un poco de contexto más aquí es otra forma en que puede ir sobre ella:

  • ¿Tienen el mayor inserción en una tabla temporal.
  • iniciar serializable tran
  • Seleccionar todas las filas temporales que ya están en la tabla de destino ...informe sobre ellos
  • Inserte los datos en la tabla temporal en la tabla real, realizando una combinación izquierda en hash e incluyendo todas las filas nuevas.
  • cometer el tran

Ese proceso es muy ligero en ida y vuelta, y teniendo en cuenta sus especificaciones debe llegar a ser muy rápido;

+0

Realmente necesito esta solución, pero no soy un usuario avanzado de SQL y no entiendo qué un serial serializable funciona. ¿Puedes entender un poco? –

1

Nota: Este es un resumen de la respuesta de Sam con un poco más de detalles

Gracias a Sam para la respuesta. Lo puse en una respuesta debido a las restricciones de espacio de los comentarios.

Derivado de su respuesta Veo dos enfoques posibles:

Solución 1:

  • tran inicio
  • agarrar todos los valores posibles de golpe "troceo" haciendo "seleccione hachís en DestinationTable donde hachís en (val1, val2, ...)
  • filtro de duplicados e informar
  • insertar datos
  • commit tran

solución 2:

  • Crear tabla temporal para reflejar la esquema de la tabla de destino
  • mayor insertar en la tabla temporal
  • iniciar la transacción serializable
  • obtener los registros duplicados : "seleccionar hash de tempTable donde tempTable.hash = destinationTable.hash"
  • informe sobre las filas duplicadas
  • insertar los datos en la tabla temporal en la tabla de destino: "select * en destinationTable de izquierda TempTable join temptable.hash = destinationTable.hash donde destinationTable.hash es nulo"
  • cometer el tran

Como tenemos dos enfoques, ¿cuál es el enfoque más optimizado? Ambos enfoques tienen que recuperar las filas duplicadas e informar mientras que el segundo enfoque requiere extra:

  • temp creación de la tabla y borrar
  • una más comandos SQL para mover datos de temperatura a la tabla de destino
  • depende del porcentaje de colisión hash, también transfiere una gran cantidad de datos innecesarios a través del cable

Si estas son las únicas soluciones, me parece que el primer enfoque gana. ¿Qué piensan ustedes? ¡Gracias!

+1

Paladín, no entiendes bien el segundo enfoque. No involucra tirar una lista hash completa al cliente y en cambio realiza el análisis duplicado en el servidor. Eso hace que escale mucho mejor. uno la tabla preexistente ya tiene una tonelada de datos. –

+0

Sam, ¿estás hablando del paso "Obtener filas duplicadas", selecciona el hash de la tabla de temptables donde tempTable.ha sh = destinationTable.hash ""? Este paso no proporciona al cliente una lista hash completa, solo los hash de coincidencia/colisión que necesito en el cliente de todos modos. Hace exactamente "Seleccionar todas las filas temporales que ya están en la tabla de destino ... informar sobre ellas". ¿Podría profundizar en el proceso de análisis duplicado? ¡Gracias! – Paladin

3

Aproximación ligeramente diferente a la que ya se ha sugerido; Realizar la SqlBulkCopy y coger el SqlException tirado:

Violation of PRIMARY KEY constraint 'PK_MyPK'. Cannot insert duplicate 
key in object 'dbo.MyTable'. **The duplicate key value is (17)**. 

entonces usted puede eliminar todos los elementos de su fuente de ID 17, el primer registro que ha sido duplicado. Estoy haciendo suposiciones aquí que se aplican a mis circunstancias y posiblemente no a las tuyas; es decir, que la duplicación es causada por los mismos datos de un error anterior SqlBulkCopy debido a errores SQL/Red durante la carga.