Điền bảng dữ liệu từ trình đọc dữ liệu


103

Tôi đang làm một điều cơ bản trong C # (MS VS2008) và có một câu hỏi nhiều hơn về thiết kế phù hợp hơn là mã cụ thể.

Tôi đang tạo một cơ sở dữ liệu và sau đó cố gắng tải dữ liệu từ một trình lưu trữ dữ liệu (dựa trên một thủ tục được lưu trữ trong SQL). Điều tôi tự hỏi là liệu cách hiệu quả nhất để tải dữ liệu là thực hiện một câu lệnh while hay có cách nào tốt hơn.

Đối với tôi, hạn chế 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 câu lệnh while của mình, nhưng tôi cũng không biết cách nào để tự động hóa điều đó vì tôi không muốn tất cả các trường từ SP chỉ chọn những trường. , nhưng đó không phải là 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 số những gì tôi làm, mặc dù đối với tôi, bản thân đoạn mã đó không đáng chú ý hoặc thậm chí là những gì tôi đang hỏi. Nếu bạn còn băn khoăn về phương pháp luận của mình, tôi sẽ dò tìm để được trợ giúp về mã sau nếu chiến lược của tôi 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());
}

Câu trả lời:


283

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

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

2
Bạn đã lưu Ngày của tôi (Y)
Uzair Xlade

1
Đây là những gì tôi đã dành một tuần để tìm kiếm!
TheTechy

17

Vui lòng kiểm tra mã dưới đây. Tự động nó sẽ chuyển đổi thà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();
        }

    }

Có một tùy chọn chuyển tiếp để tải datareader vào datatable, vậy tại sao mọi người lại sử dụng nó?
Abbas

@sarathkumar Làm tốt lắm .. tôi đang tìm mã như vậy
SimpleGuy

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

dt.Load(reader)cũng không phải lúc nào cũng hoạt động-Tôi sẽ gặp những Object reference not set to an instance of an objectlỗi khó chịu đó , có thể là khi tôi không lấy lại được hàng nào. Một cái gì đó hướng dẫn sử dụng như thế này có ích. Tôi đã thử nó và phải loại bỏ những column.dòng đó trong dtSchema foreachvòng lặp vì nó nói rằng đó là một diễn viên bất hợp pháp để tiếp booltục (bool)drow["IsUnique"]. Tôi không cần chúng, lấy tên cột để điền vào cái mới DataTablelà đủ. Điều này đã quản lý để giúp tôi khắc phục sự cố ds.Fill(adapter)mà tôi không thể tải một bảng lớn SELECT * FROM MyTable.
vapcguy

Một lưu ý - nếu có giá trị null trong bất kỳ cột nào, chúng phải được xử lý hoặc hàm này gây ra ngoại lệ. Phải kiểm tra if (!dr.IsDBNull(i))như điều tiếp theo bên trong forvòng lặp đó . Sau đó bạn làm công việc của bạn dataRow. Nhưng sau đó bạn cần một elsetrên đó, trong trường hợp bạn tìm thấy một giá trị rỗng. Nếu bạn làm vậy, bạn phải tìm ra loại cột bạn đang thêm và chỉ định giá trị null cho phù hợp (nghĩa là bạn có thể gán String.Emptynếu nó thuộc loại System.String, nhưng bạn phải chỉ định 0nếu đó là System.Int16(trường Boolean) hoặc System.Decimal.
vapcguy

13

Nếu bạn đang cố gắng tải a DataTable, thì hãy tận dụng SqlDataAdapter:

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 xác định các cột. Chỉ cần tạo DataTableFillnó.

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


1
Vấn đề duy nhất ở đây là nếu bạn tìm thấy một cột / giá trị gây ra 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 SqlDataReadervà đọc chúng bằng cách sử dụng một vòng lặp qua các trường.
vapcguy

9

Như Sagi đã nêu trong câu trả lời của họ DataTable.Load là một giải pháp tốt. Nếu bạn đang cố gắng tải nhiều bảng từ một trình đọc duy nhất, bạn không cần gọi DataReader.NextResult. Phương thức DataTable.Load cũng đưa người đọc đế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);
}

5

Tôi cũng đã xem xét vấn đề này và sau khi so sánh phương thức SqlDataAdapter.Fill với các chức năng SqlDataReader.Load, tôi nhận thấy rằng phương thức SqlDataAdapter.Fill nhanh hơn gấp đôi so với các tập kết quả mà tôi đang sử dụng.

Mã đã sử dụng:

    [TestMethod]
    public void SQLCommandVsAddaptor()
    {
        long AdapterFillLargeTableTime, readerLoadLargeTableTime, AdapterFillMediumTableTime, readerLoadMediumTableTime, AdapterFillSmallTableTime, readerLoadSmallTableTime, AdapterFillTinyTableTime, 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
            AdapterFillLargeTableTime = MeasureExecutionTimeMethod(sconn, LargeTableToFill, ExecuteDataAdapterFillStep);
            readerLoadLargeTableTime = MeasureExecutionTimeMethod(sconn, LargeTableToFill, ExecuteSqlReaderLoadStep);
            // medium data set measurements
            AdapterFillMediumTableTime = MeasureExecutionTimeMethod(sconn, MediumTableToFill, ExecuteDataAdapterFillStep);
            readerLoadMediumTableTime = MeasureExecutionTimeMethod(sconn, MediumTableToFill, ExecuteSqlReaderLoadStep);
            // small data set measurements
            AdapterFillSmallTableTime = MeasureExecutionTimeMethod(sconn, SmallTableToFill, ExecuteDataAdapterFillStep);
            readerLoadSmallTableTime = MeasureExecutionTimeMethod(sconn, SmallTableToFill, ExecuteSqlReaderLoadStep);
            // tiny data set measurements
            AdapterFillTinyTableTime = 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", AdapterFillLargeTableTime);
            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", AdapterFillMediumTableTime);
            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", AdapterFillSmallTableTime);
            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", AdapterFillTinyTableTime);
            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))
        {
            // Adapter 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();
    }

Các kết quả:

10000 rows:
Sql Data Adapter 100 times table fill speed 10000 rows: 11782 milliseconds
Sql Data Reader  100 times table load speed 10000 rows: 26047 milliseconds
1000 rows:
Sql Data Adapter 100 times table fill speed 1000 rows: 984  milliseconds
Sql Data Reader  100 times table load speed 1000 rows: 2031 milliseconds
100 rows:
Sql Data Adapter 100 times table fill speed 100 rows: 125 milliseconds
Sql Data Reader  100 times table load speed 100 rows: 235 milliseconds
10 rows:
Sql Data Adapter 100 times table fill speed 10 rows: 32 milliseconds
Sql Data Reader  100 times table load speed 10 rows: 93 milliseconds

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.