2010-07-29 18 views
5

Por lo tanto, estoy trabajando en una base de datos que agregaré a mis futuros proyectos como una especie de db de soporte, pero estoy teniendo un poco de problema con eso, especialmente los registros.Sql Server 2008 Sintonización de grandes transacciones (700k + filas/transacción)

La base de datos, básicamente, debe actualizarse una vez al mes. La tabla principal debe purgarse y luego rellenarse de un archivo CSV. El problema es que el Servidor SQL generará un registro para él que es MEGA grande. Logré llenarlo una vez, pero quería probar todo el proceso purgándolo y volviéndolo a llenar.

Ahí es cuando me aparece un error que indica que el archivo de registro está lleno. Salta de 88MB (después de la reducción a través del plan de mantenimiento) a 248MB y luego detiene el proceso por completo y nunca se completa.

He limitado su crecimiento a 256MB, incrementando en 16MB, razón por la cual falló, pero en realidad no necesito registrar nada en absoluto. ¿Hay alguna forma de eludir por completo el inicio de sesión en cualquier consulta que se ejecute en la base de datos?

Gracias por cualquier respuesta con anticipación!

EDIT: Por las sugerencias de @ mattmc3 Implementé SqlBulkCopy para todo el procedimiento. Funciona INCREÍBLE, excepto que mi bucle de alguna manera se cuelga en el último trozo restante que necesita insertarse. No estoy muy seguro de dónde me estoy equivocando, diablos, ni siquiera sé si este es un ciclo correcto, por lo que agradecería algo de ayuda.

Sé que es un problema con las últimas llamadas GetDataTable o SetSqlBulkCopy. Estoy tratando de insertar filas 788189, 788000 entrar y los 189 restantes están cayendo ...

string[] Rows; 

using (StreamReader Reader = new StreamReader("C:/?.csv")) { 
    Rows = Reader.ReadToEnd().TrimEnd().Split(new char[1] { 
     '\n' 
    }, StringSplitOptions.RemoveEmptyEntries); 
}; 

int RowsInserted = 0; 

using (SqlConnection Connection = new SqlConnection("")) { 
    Connection.Open(); 

    DataTable Table = null; 

    while ((RowsInserted < Rows.Length) && ((Rows.Length - RowsInserted) >= 1000)) { 
     Table = GetDataTable(Rows.Skip(RowsInserted).Take(1000).ToArray()); 

     SetSqlBulkCopy(Table, Connection); 

     RowsInserted += 1000; 
    }; 

    Table = GetDataTable(Rows.Skip(RowsInserted).ToArray()); 

    SetSqlBulkCopy(Table, Connection); 

    Connection.Close(); 
}; 

static DataTable GetDataTable(
    string[] Rows) { 
    using (DataTable Table = new DataTable()) { 
     Table.Columns.Add(new DataColumn("A")); 
     Table.Columns.Add(new DataColumn("B")); 
     Table.Columns.Add(new DataColumn("C")); 
     Table.Columns.Add(new DataColumn("D")); 

     for (short a = 0, b = (short)Rows.Length; a < b; a++) { 
      string[] Columns = Rows[a].Split(new char[1] { 
       ',' 
      }, StringSplitOptions.RemoveEmptyEntries); 

      DataRow Row = Table.NewRow(); 

      Row["A"] = Columns[0]; 
      Row["B"] = Columns[1]; 
      Row["C"] = Columns[2]; 
      Row["D"] = Columns[3]; 

      Table.Rows.Add(Row); 
     }; 

     return (Table); 
    }; 
} 

static void SetSqlBulkCopy(
    DataTable Table, 
    SqlConnection Connection) { 
    using (SqlBulkCopy SqlBulkCopy = new SqlBulkCopy(Connection)) { 
     SqlBulkCopy.ColumnMappings.Add(new SqlBulkCopyColumnMapping("A", "A")); 
     SqlBulkCopy.ColumnMappings.Add(new SqlBulkCopyColumnMapping("B", "B")); 
     SqlBulkCopy.ColumnMappings.Add(new SqlBulkCopyColumnMapping("C", "C")); 
     SqlBulkCopy.ColumnMappings.Add(new SqlBulkCopyColumnMapping("D", "D")); 

     SqlBulkCopy.BatchSize = Table.Rows.Count; 
     SqlBulkCopy.DestinationTableName = "E"; 
     SqlBulkCopy.WriteToServer(Table); 
    }; 
} 

EDITAR/código final: lo que la aplicación ya está terminado y maravillosas obras, y bastante rápido! @ mattmc3, gracias por toda la ayuda! Aquí está el código final para cualquier persona que les puede resultar útil:

List<string> Rows = new List<string>(); 

using (StreamReader Reader = new StreamReader(@"?.csv")) { 
    string Line = string.Empty; 

    while (!String.IsNullOrWhiteSpace(Line = Reader.ReadLine())) { 
     Rows.Add(Line); 
    }; 
}; 

if (Rows.Count > 0) { 
    int RowsInserted = 0; 

    DataTable Table = new DataTable(); 

    Table.Columns.Add(new DataColumn("Id")); 
    Table.Columns.Add(new DataColumn("A")); 

    while ((RowsInserted < Rows.Count) && ((Rows.Count - RowsInserted) >= 1000)) { 
     Table = GetDataTable(Rows.Skip(RowsInserted).Take(1000).ToList(), Table); 

     PerformSqlBulkCopy(Table); 

     RowsInserted += 1000; 

     Table.Clear(); 
    }; 

    Table = GetDataTable(Rows.Skip(RowsInserted).ToList(), Table); 

    PerformSqlBulkCopy(Table); 
}; 

static DataTable GetDataTable(
    List<string> Rows, 
    DataTable Table) { 
    for (short a = 0, b = (short)Rows.Count; a < b; a++) { 
     string[] Columns = Rows[a].Split(new char[1] { 
      ',' 
     }, StringSplitOptions.RemoveEmptyEntries); 

     DataRow Row = Table.NewRow(); 

     Row["A"] = ""; 

     Table.Rows.Add(Row); 
    }; 

    return (Table); 
} 

static void PerformSqlBulkCopy(
    DataTable Table) { 
    using (SqlBulkCopy SqlBulkCopy = new SqlBulkCopy(@"", SqlBulkCopyOptions.TableLock)) { 
     SqlBulkCopy.BatchSize = Table.Rows.Count; 
     SqlBulkCopy.DestinationTableName = ""; 
     SqlBulkCopy.WriteToServer(Table); 
    }; 
} 
+0

Algunas sugerencias si quiere acelerar esto aún más. 1.) En lugar de hacer Reader.ReadToEnd(), haz un ciclo y haz Reader.ReadLine() una línea a la vez. Tomará menos memoria. 2.) Si nadie accederá a su mesa durante el tiempo que la está cargando, use la opción 'SqlBulkCopyOptions.TableLock'. 3.) Solo para guardar un código, el objeto SqlBulkCopy infiere las asignaciones de columna si asigna el mismo nombre a las columnas que las que están en su tabla de destino, y como está gestionando la fragmentación usted mismo, no hay ninguna razón para configurar el .BatchSize tampoco. Feliz codificación! – mattmc3

+0

Sobre el tema de inferir las columnas, ¿funcionará si: 'DBTable = {Id (PK, IDENTIDAD), A, B, C, D}', pero 'DataTable = {A, B, C, D}'? Creo que me estaba dando problemas, por eso los especifiqué, pero, de nuevo, podría haberlo estado jodiendo de alguna manera ... – Gup3rSuR4c

+0

¡Bien, ya está! ¡Implementé todo lo que me recomendó y funciona INCREÍBLE! La memoria se redujo a la mitad a ~ 85MB y toda la operación tarda aproximadamente 45 segundos en completarse. Y descubrí las columnas de arriba, tenía razón, pero acabo de agregar un marcador de posición para el 'Id' y funcionó. '¡GRACIAS POR AYUDARME EN ESTO Y POR ENSEÑARME SOBRE COSAS QUE NUNCA SABÍA!' – Gup3rSuR4c

Respuesta

5

Si usted está haciendo una inserción masiva en la tabla en SQL Server, que es la forma en que debería estar haciendo esto (BCP, Bulk Insert, Insert Into...Select, o en .NET, la clase SqlBulkCopy) se puede utilizar el "Usuario a granel" modelo de recuperación Recomiendo leer los artículos de MSDN sobre modelos de recuperación: http://msdn.microsoft.com/en-us/library/ms189275.aspx

+0

Pensé que se suponía que el SqlBulkCopy (que es el que usaría ya que la aplicación lo realiza una aplicación de consola) se usa de una tabla a otra. – Gup3rSuR4c

+0

Oh no ... es de DataTable a una tabla de SQL Server. Cargue los datos en un objeto System.Data.DataTable que coincida con su tabla de destino. Puede obtener esos datos en el DataTable desde un archivo, desde una consulta, desde sus objetos de bus ... como lo desee. Recomiendo obtener un fragmento de 1000 o más registros allí, hacer la copia masiva a través del objeto SqlBulkCopy, y luego borrar la DataTable y hacer otra parte. Detrás de escena, el objeto SqlBulkCopy solo está utilizando las mismas facilidades que una instrucción 'Bulk Insert'. He hecho ETL de esta manera durante años y es rápido y simple. – mattmc3

+0

Ok. Estoy tratando de escribir el código ahora, nunca he usado DataTables antes y tengo que recordar los buenos métodos ol Sql porque no los he usado desde que salió Linq ... :) De todos modos, gracias por la ayuda ! – Gup3rSuR4c

1

No hay manera de evitar el uso del registro de transacciones en SQL Server.

+2

Tienes que usar el registro - eso es cierto. Pero puede minimizar el impacto del registro seleccionando modelos de recuperación alternativos y siendo estratégico acerca de cómo inserta sus datos. – mattmc3

2

Puede establecer el modelo de recuperación para cada base de datos por separado. Tal vez el simple modelo de recuperación funcionará para usted. El modelo simple:

Recupera automáticamente el espacio de registro para mantener los requisitos de espacio pequeños, lo que elimina la necesidad de administrar el espacio de registro de transacciones.

Lectura en ella here.

Cuestiones relacionadas