2013-06-10 15 views
10

Próbuję dowiedzieć się, w jaki sposób mogę poprawić wydajność wstawiania w tabeli tymczasowej w programie SQL Server przy użyciu C#. Niektórzy twierdzą, że powinienem użyć SQLBulkCopy, ale muszę zrobić coś złego, ponieważ wydaje się, że działa znacznie wolniej niż po prostu tworzenie łańcucha wstawiania SQL.Najszybszy sposób wstawienia 30 tysięcy wierszy w tabeli tymczasowej na serwerze SQL z C#

Mój kod, aby utworzyć tabelę przy użyciu SQLBulkCopy jest poniżej:

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

     } 
    } 

W ten sposób moje wstawki zabrać dużo czasu, aby uruchomić. Mam moje wkładki do szybszej pracy w inny sposób:

stworzyłem trochę wkładki jako ciąg i dołączył go w moim SQL utworzyć temp oświadczenie tabeli:

Utworzenie wkładkami ciąg:

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; 

    } 

Korzystanie z nich w SQL po utworzeniu zapytania:

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

Ponieważ widziałem ludzie mówią mi (na wymianie stosu https://dba.stackexchange.com/questions/44217/fastest-way-to-insert-30-thousand-rows-in-sql-server/44222?noredirect=1#comment78137_44222), który należy użyć S QLBulkCopy i że byłoby to szybsze, uważam, że powinienem poprawić sposób, w jaki to robię. Więc jeśli ktoś może zasugerować, w jaki sposób mogę poprawić mój kod SQLBulkCopy LUB powiedzieć, czy istnieje lepsza instrukcja wstawiania, która może poprawić wydajność mojej aplikacji, która byłaby świetna.

+0

Skąd dane na tej liście łańcuchów pochodzą w pierwszej kolejności? –

+0

To będzie zestaw kluczy, które będą pochodzić z innej aplikacji. Jeszcze nie zrobiłem tego linku.Na razie jest to tablica, którą utworzyłem z pewnymi identyfikatorami do testu. – Jenninha

+0

30 000 identyfikatorów prawdopodobnie pochodzi z bazy danych. Jeśli tak, chciałbym znaleźć sposób na to wszystko z sql. –

Odpowiedz

11

Twój problem może być w localTempTable.AcceptChanges(); Ponieważ zatwierdza twoje zmiany.
Jeśli zrobić następny, myślę, że to będzie działać szybciej

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

    } 

Od MSDN - DataSet.AcceptChanges

zobowiązuje wszystkie zmiany wprowadzone do zbioru danych, ponieważ został załadowany lub od ostatnich AcceptChanges czasu nazywano .

+0

Uruchomił się szybciej. Dzięki! – Jenninha

4

Uruchomiłem ten kod samodzielnie za pomocą obiektów StopWatch do pomiaru czasu. To AcceptChanges w każdej iteracji powoduje spowolnienie.

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

Wynik kiedy AccpetChanges jest wewnątrz pętli foreach

enter image description here

A kiedy to nie

enter image description here

Różnica jest o 3 rzędy wielkości :)

0

Zastosowanie IDataReader i to będzie działać jeszcze szybciej

zamiast cmd.ExecuteNonQuery(); Execute

cmd.ExecuteReader() 
+2

Masz rację, że użycie IDataReadera jest lepsze niż wykonanie DataTable, ale niepoprawne jest robienie 'cmd.ExectuteReader()', nadal powinno to być 'cmd.ExecuteNonQuery();' ponieważ kod w rzeczywistości nie zwraca żadnych zestawów wyników . –

+0

Masz rację, nie przeczytałeś kodu w całości – Enfantcool