2010-05-21 13 views
6

Me pregunto si hay alguna manera de hacer actualizaciones por lotes? Estoy usando ms sql server 2005.¿Cómo hacer una actualización por lotes?

Vi con el sqlDataAdaptor pero parece que primero tiene que seleccionar la instrucción con él, luego llenar un conjunto de datos y hacer cambios en el conjunto de datos.

Ahora estoy usando linq a sql para hacer la selección, así que quiero tratar de mantenerlo de esa manera. Sin embargo, es demasiado lento para hacer actualizaciones masivas. Entonces, ¿puedo mantener mi linq en sql (para la parte seleccionada) pero usando algo diferente para hacer la actualización masiva?

Gracias

Editar

Estoy interesado en esta manera tabla de ensayo, pero no estoy seguro de cómo hacerlo y todavía no está claro cómo va a ser más rápido, ya que no entiendo cómo el la parte de actualización funciona

Entonces, ¿alguien puede mostrarme cómo funcionaría esto y cómo manejar las conexiones concurrentes?

Edit2

Este fue mi último intento de tratar de hacer una actualización masiva usando XML sin embargo se utiliza para muchos recursos y mi alojamiento compartido no permite que pase a través. Así que necesito una forma diferente, por eso no estoy buscando en una mesa de preparación.

using (TestDataContext db = new TestDataContext()) 
      { 
       UserTable[] testRecords = new UserTable[2]; 
       for (int count = 0; count < 2; count++) 
       { 
        UserTable testRecord = new UserTable(); 

        if (count == 1) 
        { 
         testRecord.CreateDate = new DateTime(2050, 5, 10); 
         testRecord.AnotherField = true; 
        } 
        else 
        { 
         testRecord.CreateDate = new DateTime(2015, 5, 10); 
         testRecord.AnotherField = false; 
        } 


        testRecords[count] = testRecord; 
       } 

       StringBuilder sBuilder = new StringBuilder(); 
       System.IO.StringWriter sWriter = new System.IO.StringWriter(sBuilder); 
       XmlSerializer serializer = new XmlSerializer(typeof(UserTable[])); 
       serializer.Serialize(sWriter, testRecords);    

       using (SqlConnection con = new SqlConnection(connectionString)) 
       { 
        string sprocName = "spTEST_UpdateTEST_TEST"; 

        using (SqlCommand cmd = new SqlCommand(sprocName, con)) 
        { 
         cmd.CommandType = CommandType.StoredProcedure; 

         cmd.CommandType = System.Data.CommandType.StoredProcedure; 

         SqlParameter param1 = new SqlParameter("@UpdatedProdData", SqlDbType.VarChar, int.MaxValue); 
         param1.Value = sBuilder.Remove(0, 41).ToString(); 
         cmd.Parameters.Add(param1); 
         con.Open(); 
         int result = cmd.ExecuteNonQuery(); 
         con.Close(); 
        } 
       } 
      } 

@ Fredrik Johansson No estoy seguro de lo que dices funcionará. Me parece que quieres que haga una declaración de actualización para cada registro. No puedo hacer eso porque necesitaré actualizar 1 a 50,000+ registros y no sabré hasta ese momento.

Datos 3

Así que este es mi SP ahora. Creo que debería poder hacer conexiones concurrentes, pero quería asegurarme.

SET ANSI_NULLS ON 
GO 
SET QUOTED_IDENTIFIER ON 
GO 
CREATE PROCEDURE [dbo].[sp_MassUpdate] 
@BatchNumber uniqueidentifier 
AS 
BEGIN 
    update Product 
    set ProductQty = 50 
    from Product prod 
    join StagingTbl stage on prod.ProductId = stage.ProductId 
    where stage.BatchNumber = @BatchNumber 

    DELETE FROM StagingTbl 
    WHERE BatchNumber = @BatchNumber 

END 
+0

¿Puede explicar/justificar su comentario? ¿Problemas de rendimiento de actualización de LINQ a SQL? –

+0

Básicamente se trata de esto. Quise insertar 500 registros y actualizar 500 registros. La validación duró 1min y 58 segundos, la inserción con copia masiva tomó 2 segundos y el uso de linq a sql para actualizar las 500 filas e insertarlas duró 4 minutos. Quiero bajar eso, ya que probablemente necesite actualizar más de 30,000 a 50,000 filas. Sin embargo, no quiero conectarme con sql para hacer la selección y la manipulación, ya que me resulta más fácil trabajar con un objeto. No me importa cuando se trata de la actualización de lo que parece, ya que debería ser fácil si tengo manipulados todos los registros y puedo extraer los valores – chobo2

+0

de cada uno del objeto linq con un ciclo for y arrojarlo a lo alguna vez es requerido – chobo2

Respuesta

1

Como dijo allonym, use SqlBulkCopy, que es muy rápido (encontré mejoras de velocidad de más de 200x, de 1500 a 6 s). Sin embargo, puede usar las clases DataTable y DataRows para proporcionar datos a SQlBulkCopy (lo que parece más fácil). El uso de SqlBulkCopy de esta manera tiene la ventaja adicional de ser compatible con bein .NET 3.0 también (Linq se agregó solo en 3.5).
Pagar http://msdn.microsoft.com/en-us/library/ex21zs8x%28v=VS.100%29.aspx para obtener un código de muestra.

0

Tienes que trabajar directamente con los árboles de expresión, pero es factible. De hecho, ya se ha hecho para usted, usted sólo tiene que descargar el código fuente:

Batch Updates and Deletes with LINQ to SQL

La alternativa es usar procedimientos almacenados o especial de consultas SQL utilizando los ExecuteMethodCall y ExecuteCommand métodos de la DataContext .

+0

¿Puede usted explica esta alternativa? No me importa si la parte de actualización está en un SP o ado.net, todo lo que me importa es hasta ese punto, prefiero tener la selección y la manipulación en linq a sql para poder manejar los objetos. Después de terminar de manipularlos, no me importa cómo se actualicen en la base de datos. – chobo2

+0

@ chobo2: no estoy seguro de lo que hay que explicar: si puede escribir su lógica de actualización completamente en el servidor, como un procedimiento almacenado, simplemente arrastre ese SP a la superficie del diseñador Linq to SQL y ejecútelo. Sin embargo, no lo ayudará a grabar por registro, si está tratando de hacer diferentes actualizaciones a mil registros; la única forma de acelerar ese proceso es usar un parámetro con valores de tabla o una inserción masiva en una tabla de etapas, ninguno de los cuales es compatible con Linq to SQL. – Aaronaught

+0

¿cómo funcionaría esta tabla de etapas? – chobo2

1

Utilice SqlBulkCopy, que es muy rápido. Necesitará una implementación personalizada de IDataReader que enumera los resultados de su consulta de linq. Consulte http://code.msdn.microsoft.com/LinqEntityDataReader para obtener más información y algún código IDataReader potencialmente adecuado.

1

Puede usar sqlDataAdapter para realizar una actualización por lotes. No importa cómo llene su conjunto de datos. L2SQL o lo que sea, puedes usar diferentes métodos para hacer la actualización. Simplemente defina la consulta para ejecutar usando los datos en su tabla de datos.

La clave aquí es UpdateBatchSize. El adaptador de datos enviará las actualizaciones en lotes de cualquier tamaño que usted defina. Necesita expirar con este valor para ver qué número funciona mejor, pero los números típicos de 500-1000 son los mejores.SQL puede entonces optimizar la actualización y ejecutarla un poco más rápido. Tenga en cuenta que al hacer actualizaciones por lotes, no puede actualizar el origen de la fila de la tabla de datos.

Utilizo este método para hacer actualizaciones de 10-100K y generalmente se ejecuta en menos de 2 minutos. Sin embargo, dependerá de lo que estés actualizando.

Lo sentimos, esto está en VB ....

Using da As New SqlDataAdapter 
     da.UpdateCommand = conn.CreateCommand 
     da.UpdateCommand.CommandTimeout = 300 

     da.AcceptChangesDuringUpdate = False 
     da.ContinueUpdateOnError = False 
     da.UpdateBatchSize = 1000 ‘Expirement for best preformance 
     da.UpdateCommand.UpdatedRowSource = UpdateRowSource.None 'Needed if UpdateBatchSize > 1 
     sql = "UPDATE YourTable" 
     sql += " SET YourField = @YourField" 
     sql += " WHERE ID = @ID" 
     da.UpdateCommand.CommandText = sql 
     da.UpdateCommand.UpdatedRowSource = UpdateRowSource.None 
     da.UpdateCommand.Parameters.Clear() 
     da.UpdateCommand.Parameters.Add("@YourField", SqlDbType.SmallDateTime).SourceColumn = "YourField" 
     da.UpdateCommand.Parameters.Add("@ID", SqlDbType.SmallDateTime).SourceColumn = "ID" 

     da.Update(ds.Tables("YourTable”) 
End Using 

Otra opción es realizar un bulkcopy a una tabla temporal, y luego ejecutar una consulta para actualizar la tabla principal a partir de ella. Esto puede ser más rápido.

0

Puede usar SqlDataAdapter para realizar una actualización por lotes incluso si una tabla de datos se rellena manualmente/mediante programación (desde linq de cualquier otra fuente).

Simplemente recuerde configurar manualmente RowState para las filas en la tabla de datos. Use dataRow.SetModified() para esto.

Cuestiones relacionadas