Створіть таблицю даних із зчитувача даних


103

Я займаюся основною справою на C # (MS VS2008) і маю питання щодо правильного дизайну, ніж конкретного коду.

Я створюю таблицю даних, а потім намагаюся завантажити дані з читателя даних (який базується на збереженій процедурі SQL). Мені цікаво, чи найефективніший спосіб завантаження даних - це зробити операцію за деякий час, чи є кращий спосіб.

Для мене єдиним недоліком є ​​те, що я маю вручну вводити поля, які я хочу додати у своєму операторі while, але я також не знаю, як автоматизувати це так чи інакше, оскільки я не хочу, щоб усі поля з SP просто вибирали їх , але це не велика справа в моїх очах.

Я включив фрагменти коду нижче сукупності того, що я роблю, хоча сам код не є чудовим або навіть тим, про що я прошу. Більше того, цікавлюсь моєю методологією, я пізніше заздалегідь домагаюся допомоги в коді, якщо моя стратегія неправильна / неефективна.

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)
Uzair Xlade

1
Це те, на що я витратив тиждень на пошуки!
TheTechy

17

Перевірте наведений нижче код. Автоматично він буде конвертувати як 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 Хороша робота .. Я шукав такий код
SimpleGuy

@Abbas Coz, вбудовані дані завантажуються дуже повільно
SimpleGuy

dt.Load(reader)також не завжди спрацьовує - я отримую ці прикрі Object reference not set to an instance of an objectпомилки, ймовірно, коли я не отримую жодних рядків назад. Щось подібне до цього посібника стане в нагоді. Я спробував його і повинен був позбутися цих column.ліній в dtSchema foreachциклі , оскільки він сказав , що це було незаконне приведення до boolпро (bool)drow["IsUnique"]. Мені вони не потрібні, достатньо отримати назви стовпців, щоб заповнити нове DataTable. Це вдалося допомогти мені подолати ds.Fill(adapter)проблему, в яку я не міг завантажити велику таблицю SELECT * FROM MyTable.
vapcguy

Один застереження - якщо в будь-якому з стовпців є нульові значення, їх потрібно обробляти або ця функція спричиняє виняток. Потрібно перевірити if (!dr.IsDBNull(i))як наступну річ всередині цього forциклу. Потім ви робите свої dataRowречі. Але тоді вам знадобиться elseна це, якщо ви знайдете нуль. Якщо ви робите, ви повинні розібратися з типом стовпця, який ви додаєте, і призначити нуль відповідно (тобто ви можете призначити, String.Emptyякщо він тип System.String, але ви повинні призначити, 0якщо він System.Int16(булеве поле) або System.Decimal.
vapcguy

13

Якщо ви намагаєтеся завантажити A 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

Я також вивчив це, і порівнявши метод SqlDataAdapter.Fill з функціоналами SqlDataReader.Load, я виявив, що метод 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 та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.