Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Wie kann ich einen Ausdruck auf die FileSpec-Eigenschaft im Foreach File-Enumerator setzen?

Aus der Untersuchung, wie die ForEach-Schleife in SSIS funktioniert (im Hinblick darauf, eine eigene zu erstellen, um das Problem zu lösen), scheint es, dass die Funktionsweise (soweit ich das ohnehin sehen konnte) darin besteht, zuerst die Dateisammlung aufzuzählen, bevor eine Maske vorhanden ist spezifizierten. Es ist schwer zu sagen, was genau vor sich geht, ohne den zugrunde liegenden Code für die ForEach-Schleife zu sehen, aber es scheint so zu sein, was zu einer langsamen Leistung beim Umgang mit über 100.000 Dateien führt.

Während die Lösung von @Siva fantastisch detailliert und definitiv eine Verbesserung gegenüber meinem ursprünglichen Ansatz ist, ist es im Wesentlichen genau derselbe Prozess, außer dass eine Ausdrucksaufgabe zum Testen des Dateinamens anstelle einer Skriptaufgabe verwendet wird (dies scheint eine gewisse Verbesserung zu bieten).

Also entschied ich mich für einen völlig anderen Ansatz und anstatt eine dateibasierte ForEach-Schleife zu verwenden, zähle ich die Sammlung selbst in einem Skripttask auf, wende meine Filterlogik an und iteriere dann über die verbleibenden Ergebnisse. Folgendes habe ich getan:

In meiner Skriptaufgabe verwende ich die asynchronen DirectoryInfo.EnumerateFiles -Methode, die der empfohlene Ansatz für große Dateisammlungen ist, da sie Streaming ermöglicht, anstatt warten zu müssen, bis die gesamte Sammlung erstellt wurde, bevor irgendeine Logik angewendet wird.

Hier ist der Code:

public void Main()
{
    string sourceDir = Dts.Variables["SourceDirectory"].Value.ToString();
    int minJobId = (int)Dts.Variables["MinIndexId"].Value;

    //Enumerate file collection (using Enumerate Files to allow us to start processing immediately
    List<string> activeFiles = new List<string>();

    System.Threading.Tasks.Task listTask = System.Threading.Tasks.Task.Factory.StartNew(() =>
    {
         DirectoryInfo dir = new DirectoryInfo(sourceDir);
         foreach (FileInfo f in dir.EnumerateFiles("*.txt"))
         {
              FileInfo file = f;
              string filePath = file.FullName;
              string fileName = filePath.Substring(filePath.LastIndexOf("\\") + 1);
              int jobId = Convert.ToInt32(fileName.Substring(0, fileName.IndexOf(".txt")));

              if (jobId > minJobId)
                   activeFiles.Add(filePath);
         }
    });

    //Wait here for completion
    System.Threading.Tasks.Task.WaitAll(new System.Threading.Tasks.Task[] { listTask });
    Dts.Variables["ActiveFilenames"].Value = activeFiles;
    Dts.TaskResult = (int)ScriptResults.Success;
}

Also zähle ich die Sammlung auf, wende meine Logik an, wenn Dateien entdeckt werden, und füge den Dateipfad sofort zu meiner Liste für die Ausgabe hinzu. Sobald dies abgeschlossen ist, weise ich dies einer SSIS-Objektvariablen mit dem Namen ActiveFilenames zu die ich als Sammlung für meine ForEach-Schleife verwenden werde.

Ich habe die ForEach-Schleife als ForEach From Variable Enumerator konfiguriert , die jetzt über eine viel kleinere Sammlung iteriert (Post-filtered List<string> verglichen mit dem, was ich nur annehmen kann, war eine ungefilterte List<FileInfo> oder etwas Ähnliches im integrierten ForEach File Enumerator von SSIS .

Die Aufgaben in meiner Schleife können also nur der Verarbeitung der Daten gewidmet werden, da sie bereits gefiltert wurden, bevor sie die Schleife erreichen. Obwohl es sich weder von meinem ursprünglichen Paket noch von Sivas Beispiel zu unterscheiden scheint, scheint es in der Produktion (zumindest für diesen speziellen Fall) so, als würde das Filtern der Sammlung und das asynchrone Aufzählen einen massiven Schub gegenüber der Verwendung der integrierten ForEach-Datei bieten Zähler.

Ich werde den ForEach-Schleifencontainer weiter untersuchen und sehen, ob ich diese Logik in einer benutzerdefinierten Komponente replizieren kann. Wenn ich das hinbekomme, poste ich einen Link in den Kommentaren.