2013-09-23 19 views
62

Robię podstawową rzecz w języku C# (MS VS2008) i mam pytanie bardziej na temat właściwego projektu niż określony kod.Wypełnij tabelę danych z czytnika danych

Tworzę datatable, a następnie próbuję załadować datatable z datareadera (który jest oparty na procedurze przechowywanej SQL). Zastanawiam się, czy najbardziej efektywnym sposobem załadowania datatable jest napisanie krótkiego zdania, czy jest lepszy sposób.

Dla mnie jedyną wadą jest to, że muszę ręcznie wpisać pola, które chcę dodać w moim oświadczeniu while, ale nie wiem też, jak to zautomatyzować, ponieważ nie chcę, aby wszystkie pola z SP wybiera tylko te, ale to nie jest wielka sprawa w moich oczach.

Dołączyłem fragmenty kodu poniżej całości tego, co robię, chociaż dla mnie sam kod nie jest niezwykły lub nawet o to pytam. Moreso zastanawia się nad moją metodologią, będę się nad tym zastanawiać, jeśli moja strategia będzie zła/nieefektywna.

var dtWriteoffUpload = new DataTable(); 
dtWriteoffUpload.Columns.Add("Unit"); 
dtWriteoffUpload.Columns.Add("Year"); 
dtWriteoffUpload.Columns.Add("Period"); 
dtWriteoffUpload.Columns.Add("Acct"); 
dtWriteoffUpload.Columns.Add("Descr"); 
dtWriteoffUpload.Columns.Add("DEFERRAL_TYPE"); 
dtWriteoffUpload.Columns.Add("NDC_Indicator"); 
dtWriteoffUpload.Columns.Add("Mgmt Cd"); 
dtWriteoffUpload.Columns.Add("Prod"); 
dtWriteoffUpload.Columns.Add("Node"); 
dtWriteoffUpload.Columns.Add("Curve_Family"); 
dtWriteoffUpload.Columns.Add("Sum Amount"); 
dtWriteoffUpload.Columns.Add("Base Curr"); 
dtWriteoffUpload.Columns.Add("Ledger"); 

cmd = util.SqlConn.CreateCommand(); 
cmd.CommandTimeout = 1000; 
cmd.CommandType = CommandType.StoredProcedure; 
cmd.CommandText = "proc_writeoff_data_details"; 
cmd.Parameters.Add("@whoAmI", SqlDbType.VarChar).Value = 

WindowsIdentity.GetCurrent().Name; 

cmd.Parameters.Add("@parmEndDateKey", SqlDbType.VarChar).Value = myMostRecentActualDate; 
cmd.Parameters.Add("@countrykeys", SqlDbType.VarChar).Value = myCountryKey; 
cmd.Parameters.Add("@nodekeys", SqlDbType.VarChar).Value = "1,2"; 
break; 


dr = cmd.ExecuteReader(); 
while (dr.Read())      
{ 
    dtWriteoffUpload.Rows.Add(dr["country name"].ToString(), dr["country key"].ToString()); 
} 
+0

Duplikat pytania: http://stackoverflow.com/questions/4089471/how-do-i-film-a-datatable-using-datareader – vapcguy

Odpowiedz

179

Można załadować DataTable bezpośrednio z czytnika danych przy użyciu metody Load() że akceptuje IDataReader.

12

Jeśli próbujesz załadować DataTable, a następnie wykorzystać SqlDataAdapter zamiast:

DataTable dt = new DataTable(); 

using (SqlConnection c = new SqlConnection(cString)) 
using (SqlDataAdapter sda = new SqlDataAdapter(sql, c)) 
{ 
    sda.SelectCommand.CommandType = CommandType.StoredProcedure; 
    sda.SelectCommand.Parameters.AddWithValue("@parm1", val1); 
    ... 

    sda.Fill(dt); 
} 

Nawet nie trzeba zdefiniować kolumny. Po prostu utwórz DataTable i Fill.

Oto cString to ciąg połączenia, a sql to polecenie składowane procedury.

+0

Jedyny problem to to, że jeśli find kolumna/wartość powoduje wyjątek podczas wypełniania, nie daje żadnych szczegółów, jak możesz być w stanie uzyskać za pomocą 'SqlDataReader' i czytając je przy użyciu pętli przez pola. – vapcguy

15

Proszę sprawdzić poniższy kod. Automatycznie będzie konwertować jak DataTable

private void ConvertDataReaderToTableManually() 
    { 
     SqlConnection conn = null; 
     try 
     { 
      string connString = ConfigurationManager.ConnectionStrings["NorthwindConn"].ConnectionString; 
      conn = new SqlConnection(connString); 
      string query = "SELECT * FROM Customers"; 
      SqlCommand cmd = new SqlCommand(query, conn); 
      conn.Open(); 
      SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.CloseConnection); 
      DataTable dtSchema = dr.GetSchemaTable(); 
      DataTable dt = new DataTable(); 
      // You can also use an ArrayList instead of List<> 
      List<DataColumn> listCols = new List<DataColumn>(); 

      if (dtSchema != null) 
      { 
       foreach (DataRow drow in dtSchema.Rows) 
       { 
        string columnName = System.Convert.ToString(drow["ColumnName"]); 
        DataColumn column = new DataColumn(columnName, (Type)(drow["DataType"])); 
        column.Unique = (bool)drow["IsUnique"]; 
        column.AllowDBNull = (bool)drow["AllowDBNull"]; 
        column.AutoIncrement = (bool)drow["IsAutoIncrement"]; 
        listCols.Add(column); 
        dt.Columns.Add(column); 
       } 
      } 

      // Read rows from DataReader and populate the DataTable 
      while (dr.Read()) 
      { 
       DataRow dataRow = dt.NewRow(); 
       for (int i = 0; i < listCols.Count; i++) 
       { 
        dataRow[((DataColumn)listCols[i])] = dr[i]; 
       } 
       dt.Rows.Add(dataRow); 
      } 
      GridView2.DataSource = dt; 
      GridView2.DataBind(); 
     } 
     catch (SqlException ex) 
     { 
      // handle error 
     } 
     catch (Exception ex) 
     { 
      // handle error 
     } 
     finally 
     { 
      conn.Close(); 
     } 

    } 
+0

Istnieje prosta opcja ładowania danych do datatable, a następnie dlaczego ktoś by tego użył? – Abbas

+0

@sarathkumar Dobra robota .. Szukałem takiego kodu – SimpleGuy

+0

@Abbas Coz, wbudowany ładunek danych jest bardzo powolny – SimpleGuy

9

Jak Sagi stwierdził w swojej odpowiedzi DataTable.Load jest dobrym rozwiązaniem. Jeśli próbujesz załadować wiele tabel z jednego czytnika, nie musisz wywoływać DataReader.NextResult. Metoda DataTable.Load przesuwa również czytnik do następnego zestawu wyników (jeśli taki istnieje).

// Read every result set in the data reader. 
while (!reader.IsClosed) 
{ 
    DataTable dt = new DataTable(); 
    // DataTable.Load automatically advances the reader to the next result set 
    dt.Load(reader); 
    items.Add(dt); 
} 
2

Spojrzałem na to jak dobrze, i po porównaniu metodę SqlDataAdaptor.Fill z funcitons SqlDataReader.Load, Znalazłem, że SqlDataAdaptor.Fill metoda jest ponad dwa razy szybciej z zestawów wyników Używam

kod używany:

[TestMethod] 
    public void SQLCommandVsAddaptor() 
    { 
     long adaptorFillLargeTableTime, readerLoadLargeTableTime, adaptorFillMediumTableTime, readerLoadMediumTableTime, adaptorFillSmallTableTime, readerLoadSmallTableTime, adaptorFillTinyTableTime, readerLoadTinyTableTime; 

     string LargeTableToFill = "select top 10000 * from FooBar"; 
     string MediumTableToFill = "select top 1000 * from FooBar"; 
     string SmallTableToFill = "select top 100 * from FooBar"; 
     string TinyTableToFill = "select top 10 * from FooBar"; 

     using (SqlConnection sconn = new SqlConnection("Data Source=.;initial catalog=Foo;persist security info=True; user id=bar;password=foobar;")) 
     { 
      // large data set measurements 
      adaptorFillLargeTableTime = MeasureExecutionTimeMethod(sconn, LargeTableToFill, ExecuteDataAdapterFillStep); 
      readerLoadLargeTableTime = MeasureExecutionTimeMethod(sconn, LargeTableToFill, ExecuteSqlReaderLoadStep); 
      // medium data set measurements 
      adaptorFillMediumTableTime = MeasureExecutionTimeMethod(sconn, MediumTableToFill, ExecuteDataAdapterFillStep); 
      readerLoadMediumTableTime = MeasureExecutionTimeMethod(sconn, MediumTableToFill, ExecuteSqlReaderLoadStep); 
      // small data set measurements 
      adaptorFillSmallTableTime = MeasureExecutionTimeMethod(sconn, SmallTableToFill, ExecuteDataAdapterFillStep); 
      readerLoadSmallTableTime = MeasureExecutionTimeMethod(sconn, SmallTableToFill, ExecuteSqlReaderLoadStep); 
      // tiny data set measurements 
      adaptorFillTinyTableTime = MeasureExecutionTimeMethod(sconn, TinyTableToFill, ExecuteDataAdapterFillStep); 
      readerLoadTinyTableTime = MeasureExecutionTimeMethod(sconn, TinyTableToFill, ExecuteSqlReaderLoadStep); 
     } 
     using (StreamWriter writer = new StreamWriter("result_sql_compare.txt")) 
     { 
      writer.WriteLine("10000 rows"); 
      writer.WriteLine("Sql Data Adapter 100 times table fill speed 10000 rows: {0} milliseconds", adaptorFillLargeTableTime); 
      writer.WriteLine("Sql Data Reader 100 times table load speed 10000 rows: {0} milliseconds", readerLoadLargeTableTime); 
      writer.WriteLine("1000 rows"); 
      writer.WriteLine("Sql Data Adapter 100 times table fill speed 1000 rows: {0} milliseconds", adaptorFillMediumTableTime); 
      writer.WriteLine("Sql Data Reader 100 times table load speed 1000 rows: {0} milliseconds", readerLoadMediumTableTime); 
      writer.WriteLine("100 rows"); 
      writer.WriteLine("Sql Data Adapter 100 times table fill speed 100 rows: {0} milliseconds", adaptorFillSmallTableTime); 
      writer.WriteLine("Sql Data Reader 100 times table load speed 100 rows: {0} milliseconds", readerLoadSmallTableTime); 
      writer.WriteLine("10 rows"); 
      writer.WriteLine("Sql Data Adapter 100 times table fill speed 10 rows: {0} milliseconds", adaptorFillTinyTableTime); 
      writer.WriteLine("Sql Data Reader 100 times table load speed 10 rows: {0} milliseconds", readerLoadTinyTableTime); 

     } 
     Process.Start("result_sql_compare.txt"); 
    } 

    private long MeasureExecutionTimeMethod(SqlConnection conn, string query, Action<SqlConnection, string> Method) 
    { 
     long time; // know C# 
     // execute single read step outside measurement time, to warm up cache or whatever 
     Method(conn, query); 
     // start timing 
     time = Environment.TickCount; 
     for (int i = 0; i < 100; i++) 
     { 
      Method(conn, query); 
     } 
     // return time in milliseconds 
     return Environment.TickCount - time; 
    } 

    private void ExecuteDataAdapterFillStep(SqlConnection conn, string query) 
    { 
     DataTable tab = new DataTable(); 
     conn.Open(); 
     using (SqlDataAdapter comm = new SqlDataAdapter(query, conn)) 
     { 
      // adaptor fill table function 
      comm.Fill(tab); 
     } 
     conn.Close(); 
    } 

    private void ExecuteSqlReaderLoadStep(SqlConnection conn, string query) 
    { 
     DataTable tab = new DataTable(); 
     conn.Open(); 
     using (SqlCommand comm = new SqlCommand(query, conn)) 
     { 
      using (SqlDataReader reader = comm.ExecuteReader()) 
      { 
       // IDataReader Load function 
       tab.Load(reader); 
      } 
     } 
     conn.Close(); 
    } 

Wyniki:

10000 rzędy:
danych SQL Adapter 100 razy stołowego prędkość wypełnienia 10000 wierszy: 11782 milisekund
Sql Data Reader 100 razy prędkość ładowania stołu 10000 wierszy: 26047 milisekund
1000 wierszy:
Sql Adapter danych 100 razy tabela prędkości napełniania 1000 rzędy: 984 milisekund
danych SQL Reader 100 razy tabela prędkości obciążenia 1000 wierszy: 2031 milisekund
100 rzędów:
Sql Adapter danych 100 razy tabela prędkości napełniania 100 wierszy: 125 milisekund
danych SQL Czytnik 100 razy prędkość obciążenie stołu 100 wierszy: 235 milisekund
10 rzędów:
danych SQL Adapter 100 razy tabela prędkości napełniania 10 rzędów: 32 milisekund
danych SQL Reader 100 razy tabela prędkości obciążenia 10 rzędów: 93 milisekund

W przypadku problemów z wydajnością użycie metody SqlDataAdaptor.Fill jest znacznie bardziej wydajne. Więc jeśli nie chcesz strzelać sobie w stopę, użyj tego. Działa szybciej w małych i dużych zestawach danych.

Powiązane problemy