Mengisi tabel data dari pembaca data

103

Saya melakukan hal mendasar dalam C # (MS VS2008) dan memiliki pertanyaan lebih lanjut tentang desain yang tepat daripada kode tertentu.

Saya membuat datatable dan kemudian mencoba memuat datatable dari datareader (yang didasarkan pada prosedur tersimpan SQL). Apa yang saya ingin tahu adalah apakah cara yang paling efisien untuk memuat dataTable adalah dengan melakukan pernyataan while, atau jika ada cara yang lebih baik.

Bagi saya satu-satunya kelemahan adalah saya harus mengetik secara manual di bidang yang ingin saya tambahkan dalam pernyataan while saya, tetapi saya juga tidak tahu cara mengotomatiskannya karena saya tidak ingin semua bidang dari SP cukup pilih satu , tapi itu bukan masalah besar di mata saya.

Saya telah menyertakan potongan kode di bawah totalitas dari apa yang saya lakukan, meskipun bagi saya kode itu sendiri tidak luar biasa atau bahkan apa yang saya tanyakan. Lebih dari bertanya-tanya tentang metodologi saya, saya akan mengganggu bantuan kode nanti jika strategi saya salah / tidak efisien.

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());
}
Ryan Ward
sumber
Pertanyaan duplikat: stackoverflow.com/questions/4089471/…
vapcguy

Jawaban:

283

Anda dapat memuat DataTablelangsung dari pembaca data menggunakan Load()metode yang menerima file IDataReader.

var dataReader = cmd.ExecuteReader();
var dataTable = new DataTable();
dataTable.Load(dataReader);
Sagi
sumber
2
Anda menyelamatkan Hari (Y)
Uzair Xlade
1
Inilah yang saya cari selama seminggu!
TheTechy
17

Silakan periksa kode di bawah ini. Secara otomatis itu akan diubah sebagai 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();
        }

    }
sarathkumar
sumber
Ada opsi langsung untuk memuat datareader ke datatable, lalu mengapa ada yang menggunakan ini?
Abbas
@sarathkumar Kerja bagus .. saya sedang mencari kode seperti itu
SimpleGuy
@Abbas Coz, pemuatan data bawaan sangat lambat
SimpleGuy
dt.Load(reader)juga tidak selalu berfungsi-saya akan mendapatkan Object reference not set to an instance of an objectkesalahan yang mengganggu itu , mungkin ketika saya tidak mendapatkan baris apa pun kembali. Sesuatu yang manual seperti ini sangat berguna. Aku mencoba dan harus menyingkirkan orang-orang column.garis di dtSchema foreachlingkaran karena mengatakan itu adalah pemain ilegal untuk booldi (bool)drow["IsUnique"]. Saya tidak membutuhkannya, mendapatkan nama kolom untuk mengisi yang baru DataTablesudah cukup. Ini berhasil membantu saya mengatasi ds.Fill(adapter)masalah di mana saya tidak dapat memuat meja besar SELECT * FROM MyTable.
vapcguy
Satu peringatan - jika ada nilai null di salah satu kolom, mereka harus ditangani atau fungsi ini menyebabkan pengecualian. Harus memeriksa if (!dr.IsDBNull(i))sebagai hal berikutnya di dalam forlingkaran itu. Anda kemudian melakukan dataRowtugas Anda . Tapi kemudian Anda membutuhkan elseitu, jika Anda menemukan null. Jika Anda melakukannya, Anda harus mencari tahu jenis kolom yang Anda tambahkan, dan menetapkan null yang sesuai (yaitu Anda dapat menetapkan String.Emptyjika itu jenis System.String, tetapi Anda harus menetapkan 0apakah itu System.Int16(bidang Boolean) atau System.Decimal.
vapcguy
13

Jika Anda mencoba memuat a DataTable, manfaatkan sebagai SqlDataAdaptergantinya:

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

Anda bahkan tidak perlu menentukan kolom. Buat saja DataTabledan Fillitu.

Di sini, cStringadalah string koneksi Anda dan sqlmerupakan perintah prosedur tersimpan.

Mike Perrenoud
sumber
1
Satu-satunya masalah di sini adalah bahwa jika Anda menemukan kolom / nilai menyebabkan pengecualian selama pengisian, itu tidak memberi Anda detail apa pun, seperti Anda mungkin bisa menggunakan SqlDataReaderdan membacanya menggunakan loop melalui bidang.
vapcguy
9

Seperti yang Sagi nyatakan dalam jawaban mereka DataTable.Load adalah solusi yang baik. Jika Anda mencoba memuat beberapa tabel dari satu pembaca, Anda tidak perlu memanggil DataReader.NextResult. Metode DataTable.Load juga memajukan pembaca ke set hasil berikutnya (jika ada).

// 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);
}
ghawkes
sumber
5

Saya melihat ini juga, dan setelah membandingkan metode SqlDataAdapter.Fill dengan SqlDataReader.Load funcitons, saya telah menemukan bahwa metode SqlDataAdapter.Fill lebih dari dua kali lebih cepat dengan set hasil yang telah saya gunakan

Kode yang digunakan:

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

Hasil:

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

Untuk masalah kinerja, menggunakan metode SqlDataAdapter.Fill jauh lebih efisien. Jadi, kecuali jika Anda ingin menembak diri sendiri, gunakan itu. Ini bekerja lebih cepat untuk kumpulan data kecil dan besar.

martijn
sumber