डेटा रीडर से डेटा टेबल पॉप्युलेट करें


103

मैं C # (MS VS2008) में एक बुनियादी बात कर रहा हूं और विशिष्ट कोड की तुलना में उचित डिजाइन के बारे में अधिक प्रश्न है।

मैं एक डेटाटेबल बना रहा हूं और फिर डेटाट्रेडर को एक डेटारेडर से लोड करने की कोशिश कर रहा हूं (जो कि एसक्यूएल संग्रहित प्रक्रिया पर आधारित है)। मैं क्या सोच रहा हूं कि क्या डेटेबल को लोड करने के लिए सबसे कुशल तरीका है कि आप थोड़ी देर के लिए स्टेटमेंट कर सकते हैं, या अगर कोई बेहतर तरीका है।

मेरे लिए एकमात्र दोष यह है कि मुझे अपने फ़ील्ड में मैन्युअल रूप से टाइप करना है जिसे मैं अपने स्टेटमेंट में जोड़ना चाहता हूं, लेकिन मुझे यह भी नहीं पता है कि वैसे भी जब भी मैं एसपी से सभी फ़ील्ड नहीं चाहता हूं, तब तक केवल उन का चयन करें , लेकिन यह मेरी नजर में बहुत बड़ी बात नहीं है।

मैंने कोड स्निपेट को शामिल किया है जो मैं करता हूं, की समग्रता से नीचे, हालांकि मेरे लिए कोड स्वयं उल्लेखनीय नहीं है या यहां तक ​​कि मैं जो भी पूछ रहा हूं। मेरी कार्यप्रणाली के बारे में सोचते हुए मोरेसो ने कहा, अगर मेरी रणनीति गलत / अक्षम है, तो मैं बाद में कोड मदद के लिए तैयार हो जाऊंगा।

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

डुप्लिकेट प्रश्न: stackoverflow.com/questions/4089471/…
vapcguy

जवाबों:


283

आप DataTableकिसी डेटा रीडर से सीधे उस Load()विधि का उपयोग करके लोड कर सकते हैं जो स्वीकार करता है IDataReader

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

2
आपने मेरा दिन (Y)
उज़ैर ज़लाडे

1
यह वही है जो मैंने एक सप्ताह तक खोजा था!
theTechy

17

कृपया नीचे दिए गए कोड की जाँच करें। स्वचालित रूप से यह डेटाटेबल के रूप में परिवर्तित हो जाएगा

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 अच्छा काम .. मैं ऐसे कोड की तलाश में था
SimpleGuy

@Abbas Coz, इनबिल्ट डेटा लोड बहुत धीमा है
SimpleGuy

dt.Load(reader)यह भी हमेशा काम नहीं करता है-मैं उन pesky Object reference not set to an instance of an objectत्रुटियों को प्राप्त करूंगा, शायद जब मुझे कोई पंक्तियां वापस नहीं मिलती हैं। इस तरह कुछ मैनुअल काम आता है। मैं इसे करने की कोशिश की है और उन से छुटकारा पाने के लिए किया था column.में लाइनों dtSchema foreachपाश क्योंकि यह कहा कि यह करने के लिए एक अवैध डाली थी boolपर (bool)drow["IsUnique"]। मुझे उनकी ज़रूरत नहीं थी, नए को आबाद करने के लिए कॉलम नाम मिलना ही DataTableकाफी है। इससे मुझे एक ds.Fill(adapter)समस्या को दूर करने में मदद मिली जहां मैं एक बड़ी तालिका को लोड नहीं कर सका SELECT * FROM MyTable
वाप्गुगी

एक चेतावनी - यदि किसी भी कॉलम में अशक्त मूल्य हैं, तो उन्हें संभाला जाना चाहिए या यह फ़ंक्शन अपवाद का कारण बनता है। if (!dr.IsDBNull(i))उस forलूप के अंदर की अगली चीज के रूप में जांचना है । आप फिर अपना dataRowसामान करें। लेकिन तब आपको elseउस पर जरूरत पड़ती है, अगर आपको कोई अशक्त मिल जाए। यदि आप करते हैं, तो आप स्तंभ को जोड़ रहे हैं के प्रकार के बाहर आंकड़ा करने के लिए है, और उसके अनुसार अशक्त आवंटित (यानी आप प्रदान कर सकते हैं String.Emptyअगर यह प्रकार का है System.String, लेकिन आप आवंटित करने के लिए है 0, तो यह System.Int16(बूलियन क्षेत्र) या System.Decimal
vapcguy

13

यदि आप एक लोड करने की कोशिश कर रहे हैं DataTable, तो 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);
}

आपको कॉलम को परिभाषित करने की भी आवश्यकता नहीं है। बस DataTableऔर Fillयह बनाएँ ।

यहां, cStringआपका कनेक्शन स्ट्रिंग है और sqlसंग्रहीत कार्यविधि कमांड है।


1
यहाँ केवल समस्या यह है कि यदि आपको भरण के दौरान कोई स्तंभ / मान अपवाद का कारण बनता है, तो यह आपको कोई विवरण नहीं देता है, जैसे आप SqlDataReaderखेतों के माध्यम से लूप का उपयोग करके और उन्हें पढ़ने में सक्षम हो सकते हैं।
vapcguy

9

जैसा कि सागी ने अपने जवाब में बताया DataTable.Load एक अच्छा समाधान है। यदि आप किसी एकल रीडर से कई तालिकाओं को लोड करने का प्रयास कर रहे हैं तो आपको DataReader.NextResult को कॉल करने की आवश्यकता नहीं है। DataTable.Load विधि भी पाठक को अगले परिणाम सेट (यदि कोई हो) के लिए आगे बढ़ाती है।

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

मैंने इस पर भी ध्यान दिया, और SqlDataReader.oad funcitons के साथ SqlDataAdapter.Fill विधि की तुलना करने के बाद, मैंने पाया है कि SqlDataAdapter.Fill विधि मेरे द्वारा उपयोग किए जा रहे परिणाम सेट के साथ दोगुनी से अधिक है।

प्रयुक्त कोड:

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

परिणाम:

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

प्रदर्शन समस्याओं के लिए, SqlDataAdapter.Fill विधि का उपयोग करना कहीं अधिक कुशल है। तो जब तक आप अपने आप को पैर के उपयोग में गोली नहीं मारना चाहते हैं। यह छोटे और बड़े डेटा सेट के लिए तेजी से काम करता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.