2009-06-21 54 views
9

Estoy trabajando en una aplicación de consola para insertar datos en una base de datos MS SQL Server 2005. Tengo una lista de objetos para insertar. Aquí, he utilizado la clase Empleado como ejemplo:SQL ¿Insertar una fila o varias filas de datos?

List<Employee> employees; 

Lo que puedo hacer es insertar un objeto a la vez como esto:

foreach (Employee item in employees) 
{ 
    string sql = @"INSERT INTO Mytable (id, name, salary) 
    values ('@id', '@name', '@salary')"; 
    // replace @par with values 
    cmd.CommandText = sql; // cmd is IDbCommand 
    cmd.ExecuteNonQuery(); 
} 

O puedo crear una consulta Balk inserción de esta manera:

string sql = @"INSERT INTO MyTable (id, name, salary) "; 
int count = employees.Count; 
int index = 0; 
foreach (Employee item in employees) 
{ 
    sql = sql + string.format(
    "SELECT {0}, '{1}', {2} ", 
    item.ID, item.Name, item.Salary); 
    if (index != (count-1)) 
     sql = sql + " UNION ALL "; 
    index++ 
} 
cmd.CommandType = sql; 
cmd.ExecuteNonQuery(); 

Supongo que el último caso va a insertar filas de datos a la vez. Sin embargo, si tengo varios ks de datos, ¿hay algún límite para la cadena de consulta SQL?

No estoy seguro si una inserción con varias filas es mejor que una inserción con una fila de datos, en términos de rendimiento?

¿Alguna sugerencia para hacerlo de una mejor manera?

Respuesta

14

En realidad, de la forma en que lo tiene escrito, su primera opción será más rápida.

  1. Su segundo ejemplo tiene un problema en el mismo. Está haciendo sql = + sql + etc. Esto hará que se cree un nuevo objeto de cadena para cada iteración del ciclo. (Vea la clase StringBuilder). Técnicamente, también vas a crear un nuevo objeto de cadena en la primera instancia, pero la diferencia es que no tiene que copiar toda la información de la opción de cadena anterior.

  2. De la forma en que lo tiene configurado, SQL Server tendrá que evaluar potencialmente una consulta masiva cuando finalmente la envíe, lo que definitivamente tomará un tiempo para averiguar qué se supone que debe hacer. Debo aclarar, esto depende de cuán grande sea la cantidad de insertos que necesite hacer. Si n es pequeño, probablemente estarás bien, pero a medida que crezca tu problema empeorará.

Las inserciones masivas son más rápidas que las individuales debido a la forma en que el servidor SQL maneja las transacciones por lotes. Si va a insertar datos de C#, debe tomar la primera aproximación y ajustar cada 500 inserciones en una transacción y confirmarla, luego haga las siguientes 500 y así sucesivamente. Esto también tiene la ventaja de que si un lote falla, puede atraparlos y descubrir qué salió mal y volver a insertarlos solo. Hay otras maneras de hacerlo, pero definitivamente sería una mejora con respecto a los dos ejemplos proporcionados.

var iCounter = 0; 
foreach (Employee item in employees) 
{ 

    if (iCounter == 0) 
    { 
    cmd.BeginTransaction; 
    } 
    string sql = @"INSERT INTO Mytable (id, name, salary) 
    values ('@id', '@name', '@salary')"; 
    // replace @par with values 
    cmd.CommandText = sql; // cmd is IDbCommand 
    cmd.ExecuteNonQuery(); 
    iCounter ++; 
    if(iCounter >= 500) 
    { 
    cmd.CommitTransaction; 
    iCounter = 0; 
    } 
} 

if(iCounter > 0) 
    cmd.CommitTransaction; 
+0

¿Qué tal llamar a SP en lugar de INERTE sql? El SP tomará los parámetros similares, pero dentro del SP hay compromiso para cada llamada. ¿Se revertirá la transacción si falla alguna de las llamadas al SP? –

+0

@ Kevin, ¿por qué no CommitTransaction no aparece en Intellisense? – uSeRnAmEhAhAhAhAhA

+0

En verdad, no sé. Voy a tener que rastrear qué @Spike – kemiller2002

0

mira this thread, He respondido allí sobre el parámetro de tabla de valores.

0

Bulk-copy suele ser más rápido que hacer inserciones por su cuenta.

Si aún desea hacerlo de una de las maneras sugeridas, debe hacerlo para poder cambiar fácilmente el tamaño de las consultas que envía al servidor. De esta forma, puede optimizar la velocidad en su entorno de producción más adelante. Los tiempos de consulta pueden variar mucho según el tamaño de la consulta.

0

El tamaño del lote para una consulta de SQL Server se muestra en 65.536 * el tamaño del paquete de red. El tamaño del paquete de red es 4kbs por defecto, pero se puede cambiar. Consulte el Maximum capacity articl e para SQL 2008 para obtener el alcance. SQL 2005 también parece tener el mismo límite.

3

En MS SQL Server 2008 puede crear .Net mesa-UDT que contendrá la tabla

CREATE TYPE MyUdt AS TABLE (Id int, Name nvarchar(50), salary int) 

a continuación, puede utilizar este UDT en los procedimientos almacenados y su с # -code a inserciones por lotes. SP:

CREATE PROCEDURE uspInsert 
(@MyTvp AS MyTable READONLY) 
AS 
    INSERT INTO [MyTable] 
     SELECT * FROM @MyTvp 

C# (imagina que los registros que necesita para insertar contenidas en el cuadro "MyTable" de DataSet ds):

using(conn) 
{ 
    SqlCommand cmd = new SqlCommand("uspInsert", conn); 
    cmd.CommandType = CommandType.StoredProcedure; 

    SqlParameter myParam = cmd.Parameters.AddWithValue 
    ("@MyTvp", ds.Tables["MyTable"]); 

    myParam.SqlDbType = SqlDbType.Structured; 
    myParam.TypeName = "dbo.MyUdt"; 

    // Execute the stored procedure 
    cmd.ExecuteNonQuery(); 
} 

Por lo tanto, esta es la solución.

Por último, quiero evitar que se utilice un código como el suyo (la construcción de las cuerdas y luego ejecutar esta cadena), porque de esta forma de ejecución podrá ser utilizado para SQL-inyecciones.

Cuestiones relacionadas