Каков самый быстрый способ чтения данных из DbDataReader?
В следующем коде команда - это 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 () не имеет значения. Есть ли способ получить всю строку за один выстрел? Еще лучше, есть ли способ получить весь результат за один выстрел?
Спасибо.
Ответы
Ответ 1
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();
}
}
Ответ 2
Я немного сравнивал себя с различными подходами:
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
, как вам удобно.
Ответ 3
Я бы использовал что-то вроде dapper-dot-net, чтобы загрузить его в базовую модель типа; это микро-ORM, поэтому вы получаете преимущества метапрограммирования (эффективно созданный IL и т.д.) - без накладных расходов на такие вещи, как EF или DataTable.
Ответ 4
Вы можете использовать DbDataAdapter
, чтобы получить все результаты и сохранить их в DataTable
.
Ответ 5
Dim adapter As New Data.SqlClient.SqlDataAdapter(sqlCommand)
Dim DT As New DataTable
adapter.Fill(DT)
Ответ 6
Используйте Untyped DataSet. Это самый быстрый, насколько мне известно.