web-dev-qa-db-fra.com

Excel Interop - Efficacité et performances

Je me demandais ce que je pouvais faire pour améliorer les performances de l'automatisation Excel, car cela peut être assez lent si vous avez beaucoup de choses dans la feuille de calcul ...

En voici quelques-uns que je me suis retrouvés:

  • ExcelApp.ScreenUpdating = false - désactive le redessin de l'écran

  • ExcelApp.Calculation = Excel.XlCalculation.xlCalculationManual - désactivation du moteur de calcul afin qu'Excel ne recalcule pas automatiquement lorsqu'une valeur de cellule change (réactivez-la une fois que vous avez terminé)

  • Réduisez les appels à Worksheet.Cells.Item(row, col) et Worksheet.Range - J'ai dû interroger des centaines de cellules pour trouver la cellule dont j'avais besoin. L'implémentation d'une certaine mise en cache des emplacements des cellules a réduit le temps d'exécution de ~ 40 à ~ 5 secondes.

Quel type d'appels interopérables pèse lourdement sur les performances et doit être évité? Que pouvez-vous faire d'autre pour éviter tout traitement inutile?

62

Lorsque vous utilisez C # ou VB.Net pour obtenir ou définir une plage, déterminez quelle est la taille totale de la plage, puis obtenez un grand tableau d'objets bidimensionnels ...

//get values
object[,] objectArray = shtName.get_Range("A1:Z100").Value2;
iFace = Convert.ToInt32(objectArray[1,1]);

//set values
object[,] objectArray = new object[3,1] {{"A"}{"B"}{"C"}};
rngName.Value2 = objectArray;

Notez qu'il est important que vous sachiez quel type de données Excel stocke (texte ou nombres) car il ne le fera pas automatiquement pour vous lorsque vous reconvertirez le type à partir du tableau d'objets. Ajoutez des tests si nécessaire pour valider les données si vous n'êtes pas sûr au préalable du type de données.

47
Anonymous Type

C'est pour tous ceux qui se demandent quelle est la meilleure façon de remplir une feuille Excel à partir d'un jeu de résultats db. Il ne s'agit en aucun cas d'une liste complète, mais il énumère quelques options.

Voici quelques chiffres de performances en essayant de remplir une feuille Excel avec 155 colonnes et 4200 enregistrements sur une vieille boîte Pentium 4 3GHz, y compris le temps de récupération des données qui n'a jamais été supérieur à 10 secondes par ordre du plus lent au plus rapide ...

  1. Une cellule à la fois - n peu moins de 11 minutes

  2. Remplir un ensemble de données en le convertissant en html + Enregistrer le html sur le disque + Charger le html dans Excel et enregistrer la feuille de calcul au format xls/xlsx - 5 minutes

  3. Une colonne à la fois - 4 minutes

  4. Utilisation de la procédure sp_makewebtask obsolète dans SQL 2005 pour créer un fichier HTML - 9 secondes + Suivi en chargeant le fichier html dans Excel et en l'enregistrant en XLS/XLSX - environ 2 minutes.

  5. Convertissez l'ensemble de données .Net en ADO RecordSet et utilisez la fonction WorkSheet.Range []. CopyFromRecordset pour remplir Excel - 45 secondes!

J'ai fini par utiliser l'option 5. J'espère que cela vous aidera.

12
Ritesh

Utilisez la fonctionnalité intégrée d'Excel dans la mesure du possible, par exemple: au lieu de rechercher dans une colonne entière une chaîne donnée, utilisez la commande find disponible dans l'interface graphique par Ctrl-F:

Set Found = Cells.Find(What:=SearchString, LookIn:=xlValues, _
    SearchOrder:=xlByRows, SearchDirection:=xlNext, _
    MatchCase:=False, SearchFormat:=False)

If Not Found Is Nothing Then
    Found.Activate
    (...)
EndIf

Si vous souhaitez trier certaines listes, utilisez la commande Excel sort, ne le faites pas manuellement dans VBA:

Selection.Sort Key1:=Range("A1"), Order1:=xlAscending, Header:=xlGuess, _
    OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom, _
    DataOption1:=xlSortNormal
5
Treb

Si vous interrogez les valeurs de nombreuses cellules, vous pouvez obtenir toutes les valeurs de cellules dans une plage stockée dans un tableau de variantes d'un seul coup:

Dim CellVals() as Variant
CellVals = Range("A1:B1000").Value

Il y a un compromis ici, en termes de taille de la plage pour laquelle vous obtenez des valeurs. Je suppose que si vous avez besoin d'un millier de valeurs de cellule ou plus, c'est probablement plus rapide que de simplement parcourir différentes cellules et d'interroger les valeurs.

5
Jon Fournier

Les performances dépendent également de la façon dont vous automatisez Excel. VBA est plus rapide que l'automatisation COM est plus rapide que l'automatisation .NET. Et généralement, la liaison précoce (au moment de la compilation) est plus rapide que la liaison tardive.

Si vous rencontrez de graves problèmes de performances, vous pouvez penser à déplacer les parties critiques du code vers un module VBA et appeler ce code à partir de votre code d'automatisation COM/.NET.

Si vous utilisez .NET, vous devez également utiliser les assemblys d'interopérabilité principaux optimisés disponibles auprès de Microsoft et ne pas utiliser d'assemblys d'interopérabilité personnalisés.

2
Dirk Vollmar

Comme le dit Anonymous Type: lire/écrire des blocs de grande portée est très important pour les performances.

Dans les cas où la surcharge COM-Interop est encore trop importante, vous souhaiterez peut-être passer à l'utilisation de l'interface XLL, qui est l'interface Excel la plus rapide.

Bien que l'interface XLL soit principalement destinée aux utilisateurs C++, XL DNA et Addin Express fournissent une capacité de pont .NET vers XLL qui est nettement plus rapide que COM-Interop.

1
Charles Williams

Une autre grande chose que vous pouvez faire dans VBA est d'utiliser Option Explicit et d'éviter les variantes dans la mesure du possible. Les variantes ne sont pas évitables à 100% dans VBA, mais elles font que l'interpréteur fait plus de travail lors de l'exécution et gaspille la mémoire.

J'ai trouvé cet article très utile lorsque je commençais avec VBA dans Excel.
http://www.ozgrid.com/VBA/SpeedingUpVBACode.htm

Et ce livre

http://www.Amazon.com/VB-VBA-Nutshell-Language-OReilly/dp/1565923588

Semblable à

 app.ScreenUpdates = false //and
 app.Calculation = xlCalculationManual

vous pouvez également définir

 app.EnableEvents = false //Prevent Excel events
 app.Interactive = false  //Prevent user clicks and keystrokes

bien qu'ils ne semblent pas faire une différence aussi importante que les deux premiers.

Semblable à la définition de valeurs de plage sur des tableaux, si vous travaillez avec des données qui sont principalement des tables avec la même formule dans chaque ligne d'une colonne, vous pouvez utiliser la notation de formule R1C1 pour votre formule et définir une colonne entière égale à la chaîne de formule à définir le tout en un seul appel.

app.ReferenceStyle = xlR1C1
app.ActiveSheet.Columns(2) = "=SUBSTITUTE(C[-1],"foo","bar")"

En outre, la création de compléments XLL à l'aide d'ExcelDNA et de .NET (ou à la manière difficile en C) est également le seul moyen d'obtenir des FDU à exécuter sur plusieurs threads. (Voir la propriété IsThreadSafe de l'attribut ExcelFunction d'Excel DNA.)

Avant de passer complètement à Excel DNA, j'ai également expérimenté la création de bibliothèques COM visibles dans .NET pour référencer dans des projets VBA. Le traitement de texte lourd est un peu plus rapide que VBA de cette façon, tout comme l'utilisation de classes de liste .NET encapsulées au lieu de la collection de VBA, mais Excel DNA est mieux.

0
JamesFaix