2013-09-23 2 views
62

Ich mache eine grundlegende Sache in C# (MS VS2008) und habe eine Frage mehr über das richtige Design als spezifischen Code.Füllen Sie Datentabelle aus Datenleser

Ich erstelle eine Datentabelle und versuche dann, die Datentabelle von einem Datenreader (der auf einer gespeicherten SQL-Prozedur basiert) zu laden. Ich frage mich, ob der effizienteste Weg, die Datentabelle zu laden, eine while-Anweisung ist oder ob es einen besseren Weg gibt.

Für mich ist der einzige Nachteil, ich muss manuell die Felder eingeben, die ich in meiner while-Anweisung hinzufügen möchte, aber ich weiß auch nicht, wie man das sowieso automatisieren kann, da ich nicht alle Felder von der SP nur wählen, aber das ist nicht eine große Sache in meinen Augen.

Ich habe Code-Schnipsel unter die Totalität von dem, was ich tue, obwohl für mich der Code selbst nicht bemerkenswert ist oder sogar, worum ich frage. Wenn ich mich auch über meine Methodik wundere, werde ich später nach Code-Hilfe fragen, wenn meine Strategie falsch/ineffizient ist.

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

Doppelte Frage: http://StackOverflow.com/Questions/4089471/How-do-Ifill-a-Datatable-using-Datareader – vapcguy

Antwort

179

Sie ein DataTable direkt von einem Datenlesegerät laden können die Load() Methode, die ein IDataReader akzeptiert.

var dataReader = cmd.ExecuteReader(); 
var dataTable = new DataTable(); 
dataTable.Load(dataReader); 
12

Wenn Sie eine DataTable zu laden sind versucht, dann nutzen die SqlDataAdapter statt:

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

Sie haben nicht einmal die Spalten definieren müssen. Erstellen Sie einfach die DataTable und Fill es.

Hier ist cString Ihre Verbindungszeichenfolge und sql ist die gespeicherte Prozedur Befehl.

+0

Nur Problem hier ist, dass wenn Sie Wenn Sie eine Spalte/einen Wert finden, wird während der Füllung eine Ausnahme ausgelöst. Sie erhalten keine Details, so wie Sie möglicherweise einen 'SqlDataReader' verwenden und sie in einer Schleife durch die Felder einlesen können. – vapcguy

15

Bitte überprüfen Sie den folgenden Code. Automatisch wird es

als Datentabelle konvertiert
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

Es gibt eine direkte Option, um den Datenreader in die Datentabelle zu laden, warum würde dann jemand das verwenden? – Abbas

+0

@sarathkumar Gute Arbeit .. Ich war auf der Suche nach solchen Code – SimpleGuy

+0

@Abbas Coz, eingebaute Daten laden ist sehr langsam – SimpleGuy

9

Wie Sagi in ihrer Antwort DataTable.Load ist eine gute Lösung. Wenn Sie versuchen, mehrere Tabellen von einem einzelnen Reader zu laden, müssen Sie DataReader.NextResult nicht aufrufen. Die DataTable.Load-Methode bringt den Leser auch zur nächsten Ergebnismenge (falls vorhanden).

// 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

Ich sah dies in als auch, und nach dem SqlDataAdaptor.Fill Verfahren mit den SqlDataReader.Load funcitons zu vergleichen, habe ich, dass der SqlDataAdaptor gefunden.Fill-Methode ist mehr als doppelt so schnell mit den Ergebnismengen Ich habe

Gebrauchte Code:

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

Ergebnisse:

10000 Zeilen:
Sql Data Adapter 100 mal Tabelle Füllgeschwindigkeit 10000 Zeilen: 11782 Millisekunden
Sql Data Reader 100-fache Tabelle Ladegeschwindigkeit 10000 Zeilen: 26047 Millisekunden
1000 Zeilen:
Sql Data Adapter 100mal Tabelle Füllgeschwindigkeit 1000 Zeilen: 984 Millisekunden
SQL-Datenleser 100 mal Tabelle Laufdrehzahl 1000 Zeilen: 2031 Millisekunden
100 Zeilen:
Sql Data Adapter 100mal Tabelle Füllgeschwindigkeit 100 Zeilen: 125 Millisekunden
SQL-Datenleser 100 mal Tabelle Laufdrehzahl 100 Zeilen: 235 Millisekunden
10 Zeilen:
Sql Data Adapter 100mal Tabelle Füllgeschwindigkeit 10 Zeilen: 32 Millisekunden
SQL-Datenleser 100 mal Tischladegeschwindigkeit 10 Zeilen: 93 Millisekunden

Bei Leistungsproblemen ist die Verwendung der SqlDataAdaptor.Fill-Methode weitaus effizienter. Also, wenn du dich nicht selbst in den Fuß schießen willst, nutze das. Es funktioniert schneller für kleine und große Datensätze