Dans le code suivant, commande est une commande Db qui a déjà été configurée:
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 majeure partie du temps nécessaire à la requête avec laquelle je travaille est consacrée aux appels GetValue. Effectue-t-il un aller-retour vers la base de données pour chaque appel GetValue? Cela semble être le cas et cela semble très inefficace. Comme le note le code, essayer de le faire en une seule fois avec GetValues () ne fait aucune différence. Y a-t-il un moyen d'obtenir toute la ligne en un seul coup? Mieux encore, y a-t-il un moyen d'obtenir le résultat complet en un seul coup?
Merci.
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();
}
}
Je me suis moi-même référencé avec différentes approches:
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 et 2 renvoient DataTable
tandis que le reste est fortement typé, donc ce n'est pas exactement une pomme à l'autre, mais je les chronomètre en conséquence.
Juste l'essentiel:
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 requête a renvoyé environ 1200 lignes et 5 champs (exécutés 100 fois). À part Read1
, tous se sont bien comportés. De tout ce que je préfère, Read3
, qui retourne les données paresseusement, comme énuméré. C'est excellent pour la mémoire si vous avez seulement besoin de l'énumérer. Pour avoir une copie de la collection en mémoire, mieux vaut utiliser Read4
ou Read5
à votre guise.
Je voudrais utiliser quelque chose comme dapper-dot-net pour le charger dans un modèle de type de base; il s’agit d’un micro-ORM. Vous bénéficiez ainsi des avantages de la méta-programmation (systèmes d’activités pré-générés, etc.), sans les frais généraux liés à EF ou DataTable.
Vous pouvez utiliser une DbDataAdapter
pour obtenir tous les résultats et les stocker dans une DataTable
.
Dim adapter As New Data.SqlClient.SqlDataAdapter(sqlCommand)
Dim DT As New DataTable
adapter.Fill(DT)
Utilisez un ensemble de données non typé. C'est le plus rapide, autant que je sache.