2010-05-19 26 views
8

Tengo una lista de objetos, esta lista contiene aproximadamente 4 millones de objetos. hay un proceso almacenado que toma los atributos de los objetos como parámetros, realiza algunas búsquedas e los inserta en tablas.C# - Insertar varias filas usando un procedimiento almacenado

lo que es la forma más eficiente para insertar este 4 millones de objetos al PP?

cómo lo hago:

-- connect to sql - SQLConnection ... 

foreach(var item in listofobjects) 
{  
    SQLCommand sc = ... 

    // assign params 

    sc.ExecuteQuery();  
} 

esto ha sido muy lento.

¿hay una mejor manera de hacer esto?

este proceso será una tarea programada. correré esta hora, así que espero datos de alto volumen como este.

Respuesta

8

Tome un vistazo a la SqlBulkCopy Class

basado en su comentario, volcar los datos en una tabla de ensayo y luego hacer las operaciones de búsqueda y se insertan en la mesa puesta real en función de un proc .... será mucho más rápido de fila por fila

+0

bien sólo una nota rápida, el procedimiento almacenado. está haciendo algunas búsquedas, no está haciendo inserción directa. – DarthVader

+1

La forma normal de solucionar ese problema es hacer sus búsquedas antes de insertar. No es perfectamente transaccional, pero si está insertando 4 millones de registros, realmente no debería hacerlo de una manera que requiera aislamiento transaccional si puede evitarlo en absoluto. –

+1

+1: Esta es de lejos la mejor respuesta. Utilicé esta técnica en múltiples ocasiones exactamente como lo describió hasta en la tabla de etapas y el procedimiento almacenado. Funciona bastante bien. –

2

Nunca va a ser ideal insertar cuatro millones de registros de C#, pero una mejor manera de hacerlo es construir el texto de comando en el código para que pueda hacerlo en fragmentos.

Esto no es a prueba de balas, y no ilustra cómo incorporar las búsquedas (como se ha mencionado que necesita), pero la idea básica es:

// You'd modify this to chunk it out - only testing can tell you the right 
// number - perhaps 100 at a time. 

for(int i=0; i < items.length; i++) { 

    // e.g., 'insert dbo.Customer values(@firstName1, @lastName1)' 
    string newStatement = string.Format(
     "insert dbo.Customer values(@firstName{0}, @lastName{0})", i); 
    command.CommandText += newStatement; 

    command.Parameters.Add("@firstName" + i, items[i].FirstName); 
    command.Parameters.Add("@lastName" + i, items[i].LastName); 
} 
// ... 
command.ExecuteNonQuery(); 
+0

lo siento ... mi comentario no tiene sentido cuando miro de nuevo. Lo borrará – gbn

+0

@gbn - ¡sin daños! –

+0

@JeffSternal - ¿Tiene algún comentario sobre el rendimiento o los errores a tener en cuenta, utilizando este enfoque? Aunque SQLBulkCopy funciona, implica la creación de una tabla adicional para la organización por etapas, como otros han sugerido. Prefiero su enfoque y preferiría evitar las tablas adicionales por ciertas razones. – FMFF

1

usted podría considerar dejar caer los índices que tiene en la (s) mesa (s) en que está insertando y luego recreándolas después de haber insertado todo. No estoy seguro de cómo funciona la clase de copia masiva, pero si está actualizando sus índices en cada inserción, puede ralentizar bastante las cosas.

0
  1. Como Abe metioned: índices de caída (y recrean más adelante)
  2. Si confía en sus datos: generar una instrucción SQL para cada llamada al procedimiento almacenado, se combinan algunos, y luego ejecutar.
    Esto le ahorra gastos de comunicación.
  3. Las llamadas combinados (al procedimiento almacenado) podría ser envuelto en un BEGIN TRANSACTION por lo que tiene un solo comprometen por x inserta

Si se trata de una operación de una sola vez: no hacer a optimizar y ejecutarlo durante la noche/fin de semana

2

he tenido excelentes resultados usando XML para obtener grandes cantidades de datos en SQL Server. Al igual que usted, al principio estaba insertando filas de una en una, lo que demoró para siempre debido al tiempo de ida y vuelta entre la aplicación y el servidor, luego cambié la lógica para pasar una cadena XML que contenía todas las filas para insertar. El tiempo de inserción pasó de 30 minutos a menos de 5 segundos. Esto fue por un par de miles de filas. He probado con cadenas XML de hasta 20 megabytes de tamaño y no hubo problemas. Dependiendo del tamaño de su fila, esta podría ser una opción.

Los datos se pasa como una cadena XML utilizando el tipo nText.

Algo como esto formó los detalles básicos del procedimiento almacenado que hizo el trabajo:

CREATE PROCEDURE XMLInsertPr (ntext @XmlString)
DECLARE @ReturnStatus int, @hdoc int

EXEC @ReturnStatus = SALIDA @hdoc sp_xml_preparedocument, @XmlString
SI (@ReturnStatus <> 0)
COMENZAR
RAISERROR ('No se puede abrir el documento XML', 16,1,50003)
RETORNO @ReturnStatus
FIN

INSERT INTO TableName
SELECT * FROM OPENXML (@hdoc, '/ XMLData/Data') CON NombreTabla
FIN

+0

pero, ¿qué pasa con la búsqueda, estoy pasando algunos identificadores que deben buscarse desde otras tablas, hay alrededor de 7 búsquedas. – DarthVader

+0

A veces inserto los datos XML en una tabla temporal, luego realizo las búsquedas necesarias y las agrego a la tabla temporal. Después de que la tabla temporal está completa, todo se inserta en la tabla principal. Esto acelera la inserción en la mesa principal para reducir los problemas de bloqueo. Alternativamente, si las búsquedas son rápidas, alguna lógica de SQL adicional en la instrucción de inserción puede obtener esto sobre la marcha. Todo depende de tu comodidad con SQL:) –

+0

el dba hará una carnicería conmigo por esto :) – DarthVader

Cuestiones relacionadas