2013-06-10 13 views
10

Sto cercando di scoprire come migliorare le prestazioni degli insiemi in una tabella temporanea in SQL Server utilizzando C#. Alcune persone dicono che dovrei usare SQLBulkCopy, tuttavia, devo fare qualcosa di sbagliato in quanto sembra funzionare molto più lentamente rispetto alla semplice creazione di una stringa di inserimento SQL.Il modo più veloce per inserire 30 mila righe in una tabella temporanea su SQL Server con C#

Il mio codice per creare tabella utilizzando SqlBulkCopy è qui sotto:

public void MakeTable(string tableName, List<string> ids, SqlConnection connection) 
    { 

     SqlCommand cmd = new SqlCommand("CREATE TABLE ##" + tableName + " (ID int)", connection); 
     cmd.ExecuteNonQuery(); 

     DataTable localTempTable = new DataTable(tableName); 

     DataColumn id = new DataColumn(); 
     id.DataType = System.Type.GetType("System.Int32"); 
     id.ColumnName = "ID"; 
     localTempTable.Columns.Add(id); 

     foreach (var item in ids) 
     { 
      DataRow row = localTempTable.NewRow(); 
      row[0] = item; 
      localTempTable.Rows.Add(row); 
      localTempTable.AcceptChanges(); 
     } 


     using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connection)) 
     { 
      bulkCopy.DestinationTableName = "##" + tableName; 
      bulkCopy.WriteToServer(localTempTable); 

     } 
    } 

In questo modo i miei inserti impiegano molto tempo per l'esecuzione. Ho ottenuto il mio inserti a lavorare più velocemente in un altro modo:

ho creato il bit inserti come una stringa e si unirono nel mio SQL creare tabella temporanea dichiarazione:

Creazione di stringa di inserti:

public string prepareInserts(string tableName, List<string> ids) 
    { 
     List<string> inserts = new List<string>(); 

     var total = ids.Select(p => p).Count(); 
     var size = 1000; 

     var insert = 1; 

     var skip = size * (insert - 1); 

     var canPage = skip < total; 

     while (canPage) 
     { 
      inserts.Add(" insert into ##" + tableName + @" (ID) values " + String.Join(",", ids.Select(p => string.Format("({0})", p)) 
         .Skip(skip) 
         .Take(size) 
         .ToArray())); 
      insert++; 
      skip = size * (insert - 1); 
      canPage = skip < total; 
     } 

     string joinedInserts = String.Join("\r\n", inserts.ToArray()); 

     return joinedInserts; 

    } 

il loro utilizzo nell'istruzione SQL dopo l'interrogazione creazione:

inserts = prepareInserts(tableName, ids); 

var query = @"IF EXISTS 
              (
              SELECT * 
              FROM tempdb.dbo.sysobjects 
              WHERE ID = OBJECT_ID(N'tempdb..##" + tableName + @"') 
              ) 
               BEGIN 
                DELETE FROM ##" + tableName + @" 
               END 
              ELSE 
               BEGIN 
                CREATE TABLE ##" + tableName + @" 
                (ID int) 
               END " + inserts; 

      var command = new SqlCommand(query, sqlConnection); 
... 

Dal momento che ho visto persone che mi dicono (sullo scambio pila https://dba.stackexchange.com/questions/44217/fastest-way-to-insert-30-thousand-rows-in-sql-server/44222?noredirect=1#comment78137_44222) che dovrei usare S QLBulkCopy e sarebbe più veloce Credo che dovrei migliorare il modo in cui lo faccio. Quindi, se qualcuno può suggerire come posso migliorare il mio codice SQLBulkCopy O dimmi se c'è una migliore istruzione di inserimento che può migliorare le prestazioni della mia applicazione che sarebbe grandiosa.

+0

Da dove viene i dati per questa lista di stringhe provengono in primo luogo? –

+0

Sarà un insieme di chiavi che verranno da un'altra applicazione. Non ho ancora fatto questo collegamento.Per ora è un array che ho creato con alcuni ID per il test. – Jenninha

+0

30.000 id probabilmente provengono da un database da qualche parte. Se è così, sarei alla ricerca di un modo per fare tutto questo con sql. –

risposta

11

Il tuo problema potrebbe essere in localTempTable.AcceptChanges(); Poiché conferma le tue modifiche.
Se fate il prossimo, penso che sarà più veloce

foreach (var item in ids) 
    { 
     DataRow row = localTempTable.NewRow(); 
     row[0] = item; 
     localTempTable.Rows.Add(row); 

    } 

    localTempTable.AcceptChanges(); 

    using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connection)) 
    { 
     bulkCopy.DestinationTableName = "##" + tableName; 
     bulkCopy.WriteToServer(localTempTable); 

    } 

Da MSDN - DataSet.AcceptChanges

impegna tutte le modifiche apportate a questo DataSet da quando è stato caricato o in quanto gli ultimi AcceptChanges tempo è stato chiamato .

+0

È stato eseguito più velocemente. Grazie! – Jenninha

4

Eseguo questo codice da solo con gli oggetti StopWatch per misurare il tempo. È il AcceptChanges in ogni iterazione che rende andare lento.

public void MakeTable(string tableName, List<string> ids, SqlConnection connection) 
{ 
    SqlCommand cmd = new SqlCommand("CREATE TABLE ##" + tableName + " (ID int)", connection); 
    cmd.ExecuteNonQuery(); 

    DataTable localTempTable = new DataTable(tableName); 

    DataColumn id = new DataColumn(); 
    id.DataType = System.Type.GetType("System.Int32"); 
    id.ColumnName = "ID"; 
    localTempTable.Columns.Add(id); 

    System.Diagnostics.Stopwatch sw1 = new System.Diagnostics.Stopwatch();   

    sw1.Start(); 
    foreach (var item in ids) 
    { 
     DataRow row = localTempTable.NewRow(); 
     row[0] = item; 
     localTempTable.Rows.Add(row); 

    } 
    localTempTable.AcceptChanges(); 
    long temp1 = sw1.ElapsedMilliseconds; 
    sw1.Reset(); 
    using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connection)) 
    { 
     bulkCopy.DestinationTableName = "##" + tableName; 
     bulkCopy.WriteToServer(localTempTable); 

    } 
    long temp2 = sw1.ElapsedMilliseconds; 
} 

Risultato quando AccpetChanges è dentro foreach ciclo

enter image description here

E quando non è

enter image description here

La differenza è di 3 ordini di grandezza :)

0

Usa IDataReader e verrà eseguito ancora più veloce

invece di cmd.ExecuteNonQuery(); Execute

cmd.ExecuteReader() 
+2

Hai ragione che usare un IDataReader è meglio che fare un DataTable ma non sei corretto nel fare 'cmd.ExectuteReader()', dovrebbe comunque essere 'cmd.ExecuteNonQuery();' perché il codice non restituisce alcun set di risultati . –

+0

Hai ragione, non ho letto completamente il codice – Enfantcool

Problemi correlati