2013-02-04 5 views
0

Possible en double:
How to efficiently write to file from SQL datareader in c#?lente, grande requête SQL de C#/ASP.NET

J'essaie actuellement de créer une application Web qui utilise l'accès en lecture seule pour permettre aux utilisateurs télécharger de gros fichiers de notre base de données. La table en question contient 400 000 enregistrements et génère un fichier .csv de 50 Mo lors de l'exportation.

Il faut environ 7s pour exécuter l'instruction "SELECT * FROM [table]" sur le serveur SQL, et environ 33s pour le faire depuis mon application web (hébergée sur un serveur différent). Cela lit toutes les données dans un objet System.Data.SqlClient.SqlDataReader.

Mon problème est que je ne peux pas convertir mon SqlDataReader en un fichier .csv. La conversion de chaque ligne du SqlDataReader en une chaîne et la sortie de cette chaîne dans un fichier ligne par ligne prend presque 2 heures, ce qui est inacceptable. Voici le code que j'utilise pour créer un fichier sur le serveur de l'application Web:

while (rdr.Read()) 
    { 
     string lineout = ""; 
     for (int index = 0; index < rdr.FieldCount; index++) 
      lineout += rdr[index].ToString().Replace(',', ' ') + ','; 
     write(lineout, filename); //uses StreamWriter.WriteLine() 
    } 

Il doit y avoir un meilleur moyen. J'ai regardé autour et vu beaucoup de suggestions qui recommandent essentiellement de faire ce qui précède pour créer un fichier. Cela fonctionne très bien avec des tables plus petites, mais pas les deux très grandes que nous utilisons tous les jours. Quelqu'un peut-il me pousser dans la bonne direction?

+0

Êtes-vous sûr que ce n'est pas le SQL qui est lent? – SLaks

+0

http://stackoverflow.com/questions/9055521/how-to-e-ly-writing-to-file-from-sql-datareader-in-c – samuelesque

+0

En supposant que vous exécutez cela sur un serveur? Comment les performances de la machine réagissent-elles lorsque vous exécutez la requête? Vous n'utilisez pas de jeux de données où que vous soyez? –

Répondre

5

Vous pouvez essayer de construire votre lineout avec un StringBuilder plutôt que des chaînes concaténer manuellement:

//you can test whether it makes any difference in performance declaring a single 
//StringBuilder and clearing, or creating a new one per loop 
var sb = new StringBuilder(); 

while (rdr.Read()) 
{ 
    for (int index = 0; index < rdr.FieldCount; index++) 
     sb.Append(rdr[index].ToString().Replace(',', ' ').Append(','); 

    write(sb.ToString(), filename); //uses StreamWriter.WriteLine() 
    sb.Clear(); 
} 

Vous pouvez également essayer de simplement écrire dans le fichier directement et éviter de générer chaque ligne dans la mémoire d'abord:

//assume a StreamWriter instance has been created called sw... 
while (rdr.Read()) 
{ 
    for (int index = 0; index < rdr.FieldCount; index++) 
    { 
     sw.Write(rdr[index].ToString().Replace(',', ' '); 
     sw.WriteLine(","); 
    } 
} 

//flush and close stream 
+0

définitivement, en utilisant un StringBuilder est plus efficace que la concaténation de chaînes. pas d'allocations mémoire inutiles, pas de fragmentation de la mémoire en excès, bon point (voteup de moi) – Adi

+0

La deuxième approche est ce que j'utilise. Bien que je fais cela pour obtenir des citations délimitées et séparées par des virgules. 'sw.Write (" \ "{0} \" {1} ", rdr [index], index == rdr.FieldCount - 1?" ":", ");' Ensuite, je n'ai pas à me soucier de les virgules dans les valeurs et dans mon cas je sais déjà qu'il n'y a pas de '' 'dans les chaînes et je n'ai pas non plus besoin d'appeler ToString() –

+1

Vous êtes un bel être humain, Daniel Merci d'utiliser le code StreamWriter que vous avez fourni , le temps d'exécution est inférieur à 40s. – wmaynard