2013-09-23 68 views
62

Tôi đang làm một điều cơ bản trong C# (MS VS2008) và có câu hỏi về thiết kế phù hợp hơn mã cụ thể.Điền dữ liệu bảng từ trình đọc dữ liệu

Tôi đang tạo một datatable và sau đó cố gắng để tải datatable từ một datareader (được dựa trên một thủ tục SQL được lưu trữ). Những gì tôi tự hỏi là liệu cách hiệu quả nhất để tải datatable là để làm một tuyên bố trong khi, hoặc nếu có một cách tốt hơn.

Với tôi nhược điểm duy nhất là tôi phải nhập thủ công các trường tôi muốn thêm vào trong câu lệnh while, nhưng tôi cũng không biết cách tự động hóa anyways đó vì tôi không muốn tất cả các trường từ SP chỉ chọn những người, nhưng đó không phải là một vấn đề lớn trong mắt tôi.

Tôi đã bao gồm các đoạn mã bên dưới tổng thể của những gì tôi làm, mặc dù bản thân mã không đáng kể hoặc thậm chí những gì tôi hỏi. Moreso tự hỏi về phương pháp của tôi, tôi sẽ pester để mã giúp đỡ sau này nếu chiến lược của tôi là sai/không hiệu quả.

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

Câu hỏi trùng lặp: http://stackoverflow.com/questions/4089471/how-do-i-fill-a-datatable-using-datareader – vapcguy

Trả lời

179

Bạn có thể tải một DataTable trực tiếp từ một đầu đọc dữ liệu bằng cách sử dụng phương pháp Load() chấp nhận một IDataReader.

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

Nếu bạn đang cố gắng để tải một DataTable, sau đó tận dụng SqlDataAdapter thay vì:

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

Bạn thậm chí không cần phải xác định các cột. Chỉ cần tạo DataTableFill.

Ở đây, cString là chuỗi kết nối của bạn và sql là lệnh thủ tục được lưu trữ.

+0

Chỉ có vấn đề ở đây là nếu bạn tìm một cột/giá trị gây ra một ngoại lệ trong quá trình điền, nó không cung cấp cho bạn bất kỳ chi tiết nào, giống như bạn có thể sử dụng một 'SqlDataReader' và đọc chúng bằng cách sử dụng một vòng lặp thông qua các trường. – vapcguy

15

Vui lòng kiểm tra mã bên dưới. Tự động nó sẽ chuyển đổi như 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

Có một tùy chọn chuyển tiếp thẳng để tải trình dữ liệu vào datatable, sau đó tại sao mọi người sẽ sử dụng tính năng này? – Abbas

+0

@sarathkumar Tốt công việc .. tôi đang tìm kiếm mã số – SimpleGuy

+0

@Abbas Coz, tải dữ liệu sẵn có là rất chậm – SimpleGuy

9

Như Sagi đã nêu trong câu trả lời của họ DataTable.Load là giải pháp tốt. Nếu bạn đang cố tải nhiều bảng từ một trình đọc đơn, bạn không cần gọi DataReader.NextResult. Phương thức DataTable.Load cũng nâng người đọc lên tập kết quả tiếp theo (nếu có).

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

Tôi nhìn vào này là tốt, và sau khi so sánh các phương pháp SqlDataAdaptor.Fill với funcitons SqlDataReader.Load, tôi đã phát hiện ra rằng SqlDataAdaptor.Điền phương pháp là hơn gấp đôi nhanh chóng với bộ kết quả Tôi đã sử dụng

đã qua sử dụng mã:

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

kết quả:

10000 hàng:
Sql liệu Adaptor 100 lần bảng điền tốc độ 10000 hàng: 11782 mili giây
Trình đọc dữ liệu Sql 100 lần tốc độ tải bảng 10.000 hàng: 26047 mili giây
1000 hàng:
Sql liệu Adaptor 100 lần bảng tốc độ fill 1000 hàng: 984 mili giây
Sql dữ liệu đọc 100 bảng thời gian tải tốc độ 1000 hàng: 2031 mili giây
100 dòng:
Sql liệu Adaptor bảng 100 lần tốc độ lấp đầy 100 dòng: 125 mili giây
Sql dữ liệu đọc 100 lần bảng tốc độ tải 100 dòng: 235 mili giây
10 dòng:
Sql liệu Adaptor bảng 100 lần tốc độ lấp đầy 10 dòng: 32 mili giây
Sql dữ liệu đọc 100 bảng thời gian tải tốc độ 10 dòng: 93 mili giây

Đối với các vấn đề về hiệu năng, sử dụng phương thức SqlDataAdaptor.Fill hiệu quả hơn rất nhiều. Vì vậy, trừ khi bạn muốn tự mình bắn vào bàn chân đó. Nó hoạt động nhanh hơn cho các tập dữ liệu nhỏ và lớn

Các vấn đề liên quan