2010-07-29 9 views
5

Quindi, sto lavorando a un database che aggiungerò ai miei progetti futuri come una sorta di db di supporto, ma sto avendo un po 'di problema con esso, in particolare i registri.Sql Server 2008 Ottimizzazione con transazioni di grandi dimensioni (700k + righe/transazione)

Il database deve essere aggiornato una volta al mese. La tabella principale deve essere eliminata e quindi ricaricata da un file CSV. Il problema è che Sql Server genererà un log che è MEGA grande. Ho avuto successo nel riempirlo una volta, ma volevo testare l'intero processo eliminandolo e quindi ricaricandolo.

Ecco quando viene visualizzato un messaggio di errore relativo al completamento del file di registro. Salta da 88 MB (dopo il restringimento tramite il piano di manutenzione) a 248 MB e quindi interrompe completamente il processo e non viene mai completato.

Ho limitato la crescita a 256 MB, con un incremento di 16 MB, motivo per cui non è riuscito, ma in realtà non ho bisogno di registrare nulla. C'è un modo per bypassare completamente la registrazione su qualsiasi query eseguita sul database?

Grazie per eventuali risposte in anticipo!

MODIFICA: Per i suggerimenti di @ mattmc3 ho implementato SqlBulkCopy per l'intera procedura. Funziona INCREDIBILE, ad eccezione del fatto che il mio ciclo si arresta in qualche modo sull'ultimo chunk rimasto che deve essere inserito. Non sono troppo sicuro di dove sto andando male, diamine non so nemmeno se questo è un ciclo appropriato, quindi apprezzerei un po 'di aiuto su di esso.

So che si tratta di un problema con le ultime chiamate GetDataTable o SetSqlBulkCopy. Sto cercando di inserire 788189 righe, 788.000 entrare e il restante 189 si infrangono ...

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); 
    }; 
} 

EDIT/codice finale: Così l'App è ora finito e incredibili opere, e molto veloce! @ mattmc3, grazie per tutto l'aiuto! Ecco il codice finale per chiunque lo trovi utile:

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

Alcuni suggerimenti se si desidera accelerare ulteriormente. 1.) Invece di fare Reader.ReadToEnd(), creare un ciclo e fare Reader.ReadLine() una riga alla volta. Ci vorrà meno memoria. 2.) Se nessuno accederà al tuo tavolo durante il tempo in cui lo stai caricando, usa l'opzione 'SqlBulkCopyOptions.TableLock'. 3.Solo per risparmiare un po 'di codice, l'oggetto SqlBulkCopy deduce i tuoi mapping di colonne se assegni alle colonne lo stesso valore della tabella di destinazione, e dal momento che stai gestendo il chunking da solo, non c'è motivo di impostare anche .BatchSize. Buona programmazione! – mattmc3

+0

L'argomento di inferire le colonne funzionerà se: 'DBTable = {Id (PK, IDENTITY), A, B, C, D}', ma 'DataTable = {A, B, C, D}'? Penso che mi stessero dando problemi, ecco perché li ho specificati, ma poi di nuovo, avrei potuto rovinarlo in qualche modo ... – Gup3rSuR4c

+0

Bene, è fatta! Ho implementato tutto ciò che hai consigliato e funziona INCREDIBILE! La memoria è stata tagliata a metà a ~ 85 MB e l'intera operazione richiede circa 45 secondi. E ho scoperto le colonne sopra, avevo ragione, ma ho appena aggiunto un segnaposto per l'ID e ha funzionato. "GRAZIE PER AVERLO AIUTO A QUESTO E PER INSEGNARMI SULLE COSE CHE NON HO MAI SAPUTO !!!" – Gup3rSuR4c

risposta

5

Se si sta facendo un Inserimento di massa nella tabella in SQL Server, che è come si dovrebbe fare questo (BCP, Bulk Insert, Insert Into...Select, o in .NET, la classe SqlBulkCopy) è possibile utilizzare il "Bulk Logged" modello di recupero. Consiglio vivamente di leggere gli articoli MSDN sui modelli di recupero: http://msdn.microsoft.com/en-us/library/ms189275.aspx

+0

Ho pensato che lo SqlBulkCopy (che è quello che userei da quando l'operazione viene eseguita da un'app console) dovrebbe essere usato da Table a Table? – Gup3rSuR4c

+0

Oh no ... proviene da DataTable a una tabella di SQL Server. Carichi i dati in un oggetto System.Data.DataTable che corrisponde alla tabella di destinazione. È possibile ottenere tali dati nel DataTable da un file, da una query, dagli oggetti di busniess ... tuttavia si desidera. Raccomando di ottenere una porzione di circa 1000 record, eseguendo la copia bulk tramite l'oggetto SqlBulkCopy e quindi cancellando il DataTable e facendo un altro blocco. Dietro le quinte, l'oggetto SqlBulkCopy utilizza solo le stesse funzionalità dell'istruzione 'Bulk Insert'. Ho fatto ETL in questo modo per anni ed è veloce e semplice. – mattmc3

+0

Ok. Sto cercando di scrivere il codice ora, non ho mai usato DataTable prima e ho bisogno di ricordare i buoni metodi di Sql perché non li ho usati da quando Linq è uscito ... :) Comunque, grazie per l'aiuto ! – Gup3rSuR4c

1

Non è possibile ignorare l'utilizzo del registro delle transazioni in SQL Server.

+2

Devi usare il registro - è vero. Tuttavia, è possibile ridurre al minimo l'impatto della registrazione selezionando modelli di recupero alternativi ed essendo strategici sul modo in cui si inseriscono i dati. – mattmc3

2

È possibile impostare separatamente il modello Recupera per ciascun database. Forse il modello di ripristino semplice funzionerà per te. Il modello semplice:

Recupera automaticamente lo spazio di registrazione per mantenere piccoli requisiti di spazio, eliminando sostanzialmente la necessità di gestire lo spazio del log delle transazioni.

Leggere su di esso here.

Problemi correlati