Какой самый быстрый способ чтения данных из DbDataReader? - PullRequest
18 голосов
/ 22 апреля 2011

В следующем коде команда является командой DbCommand, которая уже была настроена:

using( var dataReader = command.ExecuteReader() /*The actual execution of the query takes relatively little time.*/ ) {
                while( dataReader.Read() ) {
                    // These are what take all of the time. Replacing them all with reader.GetValues( myArray ) has no impact.
                    val0 = dataReader.GetValue( 0 );
                    val1 = dataReader.GetValue( 1 );
                    val2 = dataReader.GetValue( 2 );
                }
            }

Большая часть времени для запроса, с которым я сейчас работаю, расходуется на вызовы GetValue. Делает ли это обратное путешествие в базу данных для каждого вызова GetValue? Кажется, что это так, и это кажется очень неэффективным. Как отмечается в коде, попытка сделать это за один раз с помощью GetValues ​​() не имеет значения. Есть ли способ получить весь ряд одним выстрелом? А еще лучше, есть ли способ получить весь набор результатов за один выстрел?

Спасибо.

Ответы [ 6 ]

34 голосов
/ 13 февраля 2013

Я провел несколько сравнительных тестов с различными подходами:

public DataTable Read1(string query)
{
    using (var cmd = conn.CreateCommand())
    {
        cmd.CommandText = query;
        cmd.Connection.Open();
        var table = new DataTable();
        using (var r = cmd.ExecuteReader())
            table.Load(r);
        return table;
    }
}

public DataTable Read2<S>(string query) where S : IDbDataAdapter, IDisposable, new()
{
    using (var da = new S())
    {
        using (da.SelectCommand = conn.CreateCommand())
        {
            da.SelectCommand.CommandText = query;
            DataSet ds = new DataSet();
            da.Fill(ds);
            return ds.Tables[0];
        }
    }
}

public IEnumerable<S> Read3<S>(string query, Func<IDataRecord, S> selector)
{
    using (var cmd = conn.CreateCommand())
    {
        cmd.CommandText = query;
        cmd.Connection.Open();
        using (var r = cmd.ExecuteReader())
            while (r.Read())
                yield return selector(r);
    }
}

public S[] Read4<S>(string query, Func<IDataRecord, S> selector)
{
    using (var cmd = conn.CreateCommand())
    {
        cmd.CommandText = query;
        cmd.Connection.Open();
        using (var r = cmd.ExecuteReader())
            return ((DbDataReader)r).Cast<IDataRecord>().Select(selector).ToArray();
    }
}

public List<S> Read5<S>(string query, Func<IDataRecord, S> selector)
{
    using (var cmd = conn.CreateCommand())
    {
        cmd.CommandText = query;
        cmd.Connection.Open(); 
        using (var r = cmd.ExecuteReader())
        {
            var items = new List<S>();
            while (r.Read())
                items.Add(selector(r));
            return items;
        }
    }
}

1 и 2 возвращают DataTable, в то время как остальные строго типизировали набор результатов, так что это точно не яблоки с яблоками, а я их время соответственно.

Только самое необходимое:

Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < 100; i++)
{
    Read1(query); // ~8900 - 9200ms

    Read1(query).Rows.Cast<DataRow>().Select(selector).ToArray(); // ~9000 - 9400ms

    Read2<MySqlDataAdapter>(query); // ~1750 - 2000ms

    Read2<MySqlDataAdapter>(query).Rows.Cast<DataRow>().Select(selector).ToArray(); // ~1850 - 2000ms

    Read3(query, selector).ToArray(); // ~1550 - 1750ms

    Read4(query, selector); // ~1550 - 1700ms

    Read5(query, selector); // ~1550 - 1650ms
}

sw.Stop();
MessageBox.Show(sw.Elapsed.TotalMilliseconds.ToString());

Запрос вернул около 1200 строк и 5 полей (выполнено 100 раз).Кроме Read1 все хорошо.Из всех я предпочитаю Read3, который возвращает данные лениво, как перечислено.Это хорошо для памяти, если вам нужно только перечислить его.Чтобы иметь копию коллекции в памяти, лучше использовать Read4 или Read5, если хотите.

5 голосов
/ 22 апреля 2011
using (connection)
    {
        SqlCommand command = new SqlCommand(
          "SELECT CategoryID, CategoryName FROM dbo.Categories;" +
          "SELECT EmployeeID, LastName FROM dbo.Employees",
          connection);
        connection.Open();

        SqlDataReader reader = command.ExecuteReader();

        while (reader.HasRows)
        {
            Console.WriteLine("\t{0}\t{1}", reader.GetName(0),
                reader.GetName(1));

            while (reader.Read())
            {
                Console.WriteLine("\t{0}\t{1}", reader.GetInt32(0),
                    reader.GetString(1));
            }
            reader.NextResult();
        }
    }
4 голосов
/ 22 апреля 2011

Я бы использовал что-то вроде dapper-dot-net , чтобы загрузить его в модель базового типа;это микро-ORM, поэтому вы получаете преимущества метапрограммирования (эффективно предварительно сгенерированного IL и т. д.) - без дополнительных затрат, таких как EF или DataTable.

1 голос
/ 22 апреля 2011
        Dim adapter As New Data.SqlClient.SqlDataAdapter(sqlCommand)
        Dim DT As New DataTable
        adapter.Fill(DT)
1 голос
/ 22 апреля 2011

Вы можете использовать DbDataAdapter, чтобы получить все результаты и сохранить их в DataTable.

0 голосов
/ 31 марта 2015

Использовать нетипизированный набор данных.Насколько я знаю, это быстрее всего.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...