Veri tablosunu veri okuyucudan doldurun


103

C # 'da (MS VS2008) temel bir şey yapıyorum ve belirli bir koddan çok doğru tasarım hakkında bir sorum var.

Bir datatable oluşturuyorum ve daha sonra datatable'ı bir veri okuyucusundan (SQL saklı yordamına dayanan) yüklemeye çalışıyorum. Merak ettiğim şey, datatable'ı yüklemenin en verimli yolunun bir while cümlesi yapmak mı yoksa daha iyi bir yolu olup olmadığıdır.

Benim için tek dezavantajı while ifademde eklemek istediğim alanları manuel olarak yazmam gerekmesidir, ancak bunu otomatikleştirmenin bir yolunu da bilmiyorum, çünkü SP'deki tüm alanları istemiyorum, sadece birini seç ama bu benim gözümde çok büyük bir olay değil.

Yaptığım işin toplamının altına kod parçacıkları ekledim, ancak bana göre kodun kendisi dikkate değer değil, hatta sorduğum şey bile. Yine de metodolojimi merak ederken, stratejim yanlış / verimsizse daha sonra kod yardımı için uğraşacağım.

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

Yanıtlar:


283

Bir yükleyebilir DataTablekullanarak veri okuyucusu tarafından doğrudan Load()bir kabul yöntem IDataReader.

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


1
Bir hafta aradığım şey buydu!
TheTechy

17

Lütfen aşağıdaki kodu kontrol edin. Otomatik olarak DataTable olarak dönüştürülür

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

    }

Datareader'ı datatable'a yüklemek için basit bir seçenek var, o zaman neden kimse bunu kullansın?
Abbas

@sarathkumar İyi iş .. Böyle bir kod arıyordum
SimpleGuy

@Abbas Coz, dahili veri yüklemesi çok yavaş
SimpleGuy

dt.Load(reader)ayrıca her zaman işe yaramıyor - bu sinir bozucu Object reference not set to an instance of an objecthataları muhtemelen herhangi bir satır geri alamadığımda alıyorum. Bunun gibi bir el kitabı kullanışlı oluyor. Ben denedim ve bunlardan kurtulmak zorunda column.satırlara dtSchema foreachbunun için yasadışı dökme olduğunu söyledi çünkü döngü boolüzerinde (bool)drow["IsUnique"]. Onlara ihtiyacım yoktu, yeniyi doldurmak için sütun adlarını almak DataTableyeterli. Bu ds.Fill(adapter), büyük bir masayı yükleyemediğim bir sorunun üstesinden gelmeme yardımcı oldu SELECT * FROM MyTable.
vapcguy

Bir uyarı - sütunlardan herhangi birinde boş değerler varsa, bunların ele alınması gerekir veya bu işlev bir istisnaya neden olur. Bu döngüdeki bir if (!dr.IsDBNull(i))sonraki şey olarak kontrol etmelisin for. Sonra işini yaparsın dataRow. Ama sonra elsebir boş bulmanız durumunda, bunun üzerine bir ihtiyacınız var . Bunu yaparsanız, eklemekte olduğunuz sütun türüne dışarı şekle sahiptir ve buna göre null adlı atamak (yani Atayabileceğiniz String.Emptydeğil, yazın ise System.String, ancak atamak zorunda 0eğer en System.Int16(Boole alanı) veya System.Decimal.
vapcguy

13

Bir yüklemeye çalışıyorsanız DataTable, SqlDataAdapterbunun yerine kaldıraç kullanın:

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

Sütunları tanımlamanıza bile gerek yok. Sadece DataTableve Fillonu oluşturun.

Burada, cStringbağlantı dizeniz ve sqlsaklı yordam komutudur.


1
Buradaki tek sorun, bir sütun / değerin doldurma sırasında bir istisnaya neden olduğunu bulursanız, size herhangi bir ayrıntı vermez, örneğin a SqlDataReaderkullanıp alanlar arasında bir döngü kullanarak bunları okuyabilirsiniz.
vapcguy

9

Sagi'nin DataTable yanıtında belirttiği gibi.Load iyi bir çözüm. Tek bir okuyucudan birden çok tablo yüklemeye çalışıyorsanız, DataReader.NextResult'u çağırmanıza gerek yoktur. DataTable.Load yöntemi ayrıca okuyucuyu sonraki sonuç kümesine (varsa) ilerletir.

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

Buna da baktım ve SqlDataAdapter.Fill yöntemini SqlDataReader.Load işlevleriyle karşılaştırdıktan sonra, SqlDataAdapter.Fill yönteminin, kullandığım sonuç kümeleriyle iki kattan daha hızlı olduğunu buldum

Kullanılan kod:

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

Sonuçlar:

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

Performans sorunları için SqlDataAdapter.Fill yöntemini kullanmak çok daha verimlidir. Yani kendini ayağından vurmak istemiyorsan bunu kullan. Küçük ve büyük veri kümeleri için daha hızlı çalışır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.