9

何年にもわたるファイルを含むディレクトリからファイルを処理するSSISパッケージを作成しようとしています。ファイルにはすべて数値の名前が付けられているため、すべての処理を節約するために、SSISに最小数を渡し、名前(数に変換)が最小値よりも大きいファイルのみを列挙します。

ForEachファイルループですべてを列挙してからスクリプトタスクでファイルを除外しようとしましたが、数十万のファイルを処理する場合、これは遅すぎて適切ではありません。

FileSpecプロパティを使用すると、コレクションに含めるファイルを指定するファイルマスクを指定できますが、基本的に文字列が一致するため、式を指定してそれを機能させる方法がわかりません。

コンポーネント内のどこかに基本的に言う表現があればShould I Enumerate? - Yes / No、それは完璧でしょう。以下の式を試しましたが、適用するプロパティが見つかりません。

(DT_I4)REPLACE(SUBSTRING(@ [User :: ActiveFilePath]、FINDSTRING(@ [User :: ActiveFilePath]、 "\"、7)+ 1,100)、"。txt"、 "")> @ [ユーザー: :MinIndexId]?"真/偽"

4

3 に答える 3

16

これを実現する方法の 1 つを次に示します。Expression Taskと組み合わせて使用​​しForeach Loop Containerて、ファイル名の数値を一致させることができます。これを行う方法を示す例を次に示します。サンプルは を使用していSSIS 2012ます。

これはあまり効率的ではないかもしれませんが、これを行う 1 つの方法です。

YYYYMMDD という形式で名前が付けられた多数のファイルを含むフォルダーがあるとします。フォルダーには、 192101011921020119210301など、1921 年以降の毎月の最初の日のファイルが含まれてます。それはファイルを追加し1,103ます。

要件は、1948 年 6 月以降に作成されたファイルのみをループすることであるとしましょう。つまり、SSIS パッケージは より大きいファイルのみをループする必要があります19480601

ファイル

SSIS パッケージで、次の 3 つのパラメーターを作成します。これらの値は環境全体で構成できるため、これらのパラメーターを構成することをお勧めします。

  • ExtensionToMatch- データ型のこのパラメーターにはString、パッケージがループする必要がある拡張子が含まれます。FileSpecこれにより、Foreach ループ コンテナーで使用される変数に値が追加されます。

  • FolderToEnumerate- データ型のこのパラメーターはString、ループするファイルを含むフォルダー パスを格納します。

  • MinIndexId- データ型のこのパラメーターにはInt32、ファイルがパターンに一致する最小数値が含まれます。

パラメーター

ファイルをループするのに役立つ次の 4 つのパラメーターを作成します。

  • ActiveFilePath- このデータ型の変数はString、Foreach ループ コンテナーがフォルダー内の各ファイルをループするときにファイル名を保持します。この変数は、別の変数の式で使用されます。エラーを回避するには、空でない値、たとえば 1 に設定します。

  • FileCountInt32- これは、Foreach ループ コンテナーがループするファイルの数を示すために、このサンプルで使用されるデータ型のダミー変数です。

  • FileSpec- データ型のこの変数はString、ループするファイル パターンを保持します。この変数の式を以下の値に設定します。この式は、パラメーターで指定された拡張子を使用します。拡張子がない場合は、*.*すべてのファイルをループします。

"*" + (@[$Package::ExtensionToMatch] == "" ? ".*" : @[$Package::ExtensionToMatch])

  • ProcessThisFile- データ型のこの変数はBoolean、特定のファイルが条件に一致するかどうかを評価します。

変数

以下に示すようにパッケージを構成します。Foreach ループ コンテナーは、FileSpec変数で指定されたパターンに一致するすべてのファイルをループします。式タスクで指定された式は実行時に評価され、変数 ProcessThisFile に入力されます。この変数は、優先順位制約で使用され、ファイルを処理するかどうかを決定します。

Foreach ループ コンテナー内のスクリプト タスクはFileCount、式に正常に一致するファイルごとに変数のカウンターを 1 ずつ増やします。

Foreach ループの外側のスクリプト タスクは、Foreach ループ コンテナーによってループされたファイルの数を表示するだけです。

制御フロー

パラメーターを使用してフォルダーをループし、変数を使用してファイルをループするように Foreach ループ コンテナーを構成します。

Foreach ループ コレクション

ActiveFilePathループが各ファイルを通過するときに、ファイル名を変数に格納します。

Foreach ループ変数のマッピング

式タスクで、式を次の値に設定します。式は、拡張子のないファイル名を数値に変換し、パラメーターで指定された数値よりも大きいと評価されるかどうかを確認します。MinIndexId

@[User::ProcessThisFile] = (DT_BOOL)((DT_I4)(REPLACE(@[User::ActiveFilePath], @[User::FileSpec] ,"")) > @[$Package::MinIndexId] ? 1: 0)

式タスク

Precedence 制約を右クリックしProcessThisFile、式で変数を使用するように構成します。これにより、式タスクで設定された条件に一致する場合にのみファイルを処理するようパッケージに指示します。

@[ユーザー::ProcessThisFile]

優先順位の制約

最初のスクリプト タスクでは、変数をReadWriteVariablesUser::FileCountに設定し、スクリプト タスク内に次の C# コードを記述します。これにより、条件に正常に一致したファイルのカウンターがインクリメントされます。

public void Main()
{
    Dts.Variables["User::FileCount"].Value = Convert.ToInt32(Dts.Variables["User::FileCount"].Value) + 1;
    Dts.TaskResult = (int)ScriptResults.Success;
}

2 番目のスクリプト タスクでは、変数をReadOnlyVariablesUser::FileCountに設定し、スクリプト タスク内に次の C# コードを記述します。これは、処理されたファイルの総数を単純に出力します。

public void Main()
{
    MessageBox.Show(String.Format("Total files looped through: {0}", Dts.Variables["User::FileCount"].Value));
    Dts.TaskResult = (int)ScriptResults.Success;
}

MinIndexId を設定してパッケージを実行すると1948061(これを除く)、 value が出力されます773

出力 1

MinIndexId を設定してパッケージを実行すると20111201(これを除く)、 value が出力されます11

それが役立つことを願っています。

アウトプット 2

于 2012-11-06T20:05:03.023 に答える
3

ForEach ループが SSIS でどのように機能するかを調査することから (問題を解決するために独自のループを作成することを目的として)、それが機能する方法は (とにかく見ることができる限り)、マスクが処理される前に、最初にファイル コレクションを列挙することのようです。指定。ForEach ループの基礎となるコードを見ずに何が起こっているのかを正確に判断するのは困難ですが、このように実行しているようで、10 万を超えるファイルを処理するとパフォーマンスが低下します。

@Sivaのソリューションは非常に詳細であり、私の最初のアプローチよりも確実に改善されていますが、スクリプトタスクではなく式タスクを使用してファイル名をテストすることを除いて、本質的に同じプロセスです(これはいくつかの改善を提供するようです)。

そこで、ファイルベースの ForEach ループを使用するのではなく、スクリプト タスクで自分でコレクションを列挙し、フィルタリング ロジックを適用して、残りの結果を反復処理するという、まったく異なるアプローチを取ることにしました。これは私がしたことです:

ForEach 変数列挙子にフィードするファイルを列挙するスクリプト タスクを示すサンプル制御フロー

私のスクリプト タスクでDirectoryInfo.EnumerateFilesは、ロジックを適用する前にコレクション全体が作成されるのを待つ必要がなく、ストリーミングが可能であるため、大規模なファイル コレクションに推奨される方法である非同期メソッドを使用します。

コードは次のとおりです。

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;
}

そのため、コレクションを列挙し、ファイルが検出されたときにロジックを適用し、すぐにファイル パスをリストに追加して出力します。完了したら、これをActiveFilenamesという名前の SSIS オブジェクト変数に割り当てます。これを ForEach ループのコレクションとして使用します。

ForEach ループをForEach From Variable Enumeratorとして構成しました。これは、はるかに小さなコレクションを反復するようになりました ( SSIS の組み込みのForEach File Enumeratorでフィルター処理されていないか、類似したものでList<string>あるとしか考えられないものと比較して、ポストフィルター処理されています。List<FileInfo>

したがって、ループ内のタスクは、ループに入る前に既にフィルター処理されているため、データの処理に専念できます。私の最初のパッケージまたは Siva の例と大差ないように見えますが、実稼働環境では (とにかくこの特定のケースでは)、コレクションをフィルタリングし、非同期的に列挙することで、組み込みの ForEach File を使用するよりも大幅なブーストが得られるようです。列挙子。

ForEach ループ コンテナーの調査を続け、このロジックをカスタム コンポーネントで複製できるかどうかを確認します。これが機能するようになったら、コメントにリンクを投稿します。

于 2012-11-12T17:02:39.660 に答える
1

あなたができる最善のことは、あなたが言ったように、 FileSpec を使用してマスクを指定することです。2010、2011、2012 の「201」で始まるファイルなど、少なくともいくつかの仕様を含めることができます。次に、他のタスクで、処理したくないもの (たとえば、2010) を除外できます。

于 2012-11-06T18:06:42.400 に答える