2011-04-22 17 views
17

En el siguiente código, comando es un DbCommand que ya ha sido establecido:¿Cuál es la manera más rápida de leer datos de un DbDataReader?

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

La mayor parte del tiempo para la consulta actualmente estoy trabajando con la que se gasta haciendo GetValue llama. ¿Está haciendo un viaje redondo a la base de datos para cada llamada de GetValue? Parece que sí, y esto parece muy ineficiente. Como señala el código, intentar hacerlo de una vez con GetValues ​​() no hace la diferencia. ¿Hay alguna manera de obtener toda la fila en una sola toma? Mejor aún, ¿hay alguna manera de obtener todo el conjunto de resultados en una sola toma?

Gracias.

Respuesta

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

Esto no es más rápido que el código que publiqué en la pregunta original (es más o menos el mismo), pero creo que es la más rápida de las respuestas. Al menos ahora sabemos que no hay un método más rápido que me faltaba. –

1

Puede usar DbDataAdapter para obtener todos los resultados y almacenarlos en un DataTable.

0
 Dim adapter As New Data.SqlClient.SqlDataAdapter(sqlCommand) 
     Dim DT As New DataTable 
     adapter.Fill(DT) 
4

me gustaría utilizar algo así como dapper-dot-net para cargarlo en un modelo de tipo básico; esto es un micro-ORM, por lo que obtienes los beneficios de la meta-programación (IL pregenerada eficientemente, etc.) sin la sobrecarga de cosas como EF o DataTable.

+3

No creo que esto ayude con su pregunta específica sobre los lectores de datos. Dapper usa DbDataReader.GetValue debajo del capó. –

26

he hecho un poco a mí mismo con la evaluación comparativa de diversos enfoques:

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 y 2 vuelve DataTable mientras que el resto inflexible de tipos conjunto de resultados, por lo que su exactitud no manzanas con manzanas, pero mientras el tiempo en consecuencia.

sólo lo esencial:

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

La consulta regresaron alrededor de 1200 filas y 5 campos (duración de 100 veces). Además de Read1, todos funcionaron bien. De todos prefiero Read3 que devuelve datos de forma perezosa, como se enumera. Esto es ideal para la memoria si solo necesita enumerarla. Para tener una copia de la colección en la memoria, está mejor con Read4 o Read5 a su gusto.

+1

+1 - Interesante, especialmente la comparación entre 'DataTable.Load()' y 'IDataAdapter.Fill()'. Hubiera pensado que el adaptador de datos era el mismo o más lento que cargarlo directamente en un 'DataTable', pero parece que es 4-5 veces más rápido. ¿Alguna idea de por qué? –

+0

@TimMedora ni idea, pero lo confirmé intentando con 'SQLite Connector' también que arrojó el mismo resultado que respondí [aquí] (http://stackoverflow.com/a/14807940/661933). Es tan extraño sin embargo. – nawfal

+1

@TimMedora: debe rodear el 'DataTable.Load()' con '.BeginLoadData()' y '.EndLoadData()' para lograr la misma velocidad que con 'DataSet'. –

0

Utilice Untyped DataSet. Eso es el más rápido, hasta donde yo sé.

Cuestiones relacionadas