web-dev-qa-db-fra.com

Quel est le moyen le plus rapide de lire les données d'un DbDataReader?

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.

18
Greg Smalter
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();
        }
    }
5
TechGiant

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.

33
nawfal

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.

4
Marc Gravell

Vous pouvez utiliser une DbDataAdapter pour obtenir tous les résultats et les stocker dans une DataTable.

1
Genzume
        Dim adapter As New Data.SqlClient.SqlDataAdapter(sqlCommand)
        Dim DT As New DataTable
        adapter.Fill(DT)
1
Ron Harlev

Utilisez un ensemble de données non typé. C'est le plus rapide, autant que je sache.

0
migula