【SSIS】SSIS覚書-スクリプト関連(1)

記事のフォーマットを他とあわせるついでに全体的に加筆・修正しました。

[2014/06/30追記ここまで]

SSIS覚書シリーズ一覧

前書き

なんだかSSISスクリプトタスク / スクリプトコンポーネントは敷居が高いと思われている節が(私の周りでは)あり、無理矢理既存の機能で実装する人が(私の周りでは)多いんですが、なんて言うか、控えめに言いますと、四文字言葉的なものが飛び出しそうになります。

正直言って、スクリプトを書けるか書けないかで保守性が全然違いますし、折角.NETの資産をフル活用できる環境でそれを使わないのは愚の骨頂だと思います。

そんなわけでスクリプトタスク / スクリプトコンポーネントでできることをつらつらと書いていきます。

今回は「スクリプトタスク内での変数の扱い」をテーマにします。

知っておくべきこと

まず大前提なんですが、タスクとコンポーネントの違いです。

大雑把に言うと「制御フロー内に置けるもの」がタスクで「データフロータスク内に置けるもの」がコンポーネントです。

今回解説するスクリプトタスクとスクリプトコンポーネントはどちらもC# or VB.NETを書くことができますが全くの別物です。

そしてもう一つ。SSISではそのパッケージ(or プロジェクト)内で共有する変数を設定することができます。

これはスクリプト内でも使用することができますし、値を設定することもできます。変数そのものの作り方はドキュメントを読んでください。

最後に、ある意味一番大事なんですが、SSISスクリプトC# 2.0をベースに書かれており、フレームワークの思想もそれに基づいています。そのせいで「あまりにも設計がクソすぎでしょ」と思う部分が多々出てくるかもしれませんが、諦めましょう。って言うかそろそろSSDTをWPFで書き直して欲しいですね…。

シナリオ

例えば「CSVPath」と言うStringの変数を作ったとしましょう。これは何かしらのCSVのパスが入っているとします。

もしこのパスに対し「ファイル名の末尾にシステム日付をくっつけたい(ファイル名_yyyymmdd.csvとしたい)」と言われた場合、こんなExpressionを書いて変数に格納する方法もあります。この変数を「CSVPathWithDate」としましょうか。

REPLACE(@[User::CSVPath], ".csv", SUBSTRING(REPLACE((DT_WSTR,50)GETDATE(), "-", ""), 1, 8) + ".csv")

これを見て一発で何をやってるのか判断するのは中々辛いです。

さらに「ファイル名_yyyymmdd_hhmiss.csvにしたい」と言われるとこうなります。

REPLACE(@[User::CSVPath], ".csv", SUBSTRING(REPLACE((DT_WSTR,50)GETDATE(), "-", ""), 1, 8) + "_" + SUBSTRING(REPLACE((DT_WSTR,50)GETDATE(), ":", ""), 12, 6) + ".csv")

こんなExpressionを書いても別にいいんですけど、可読性と言う意味では最悪です。

それならスクリプトタスクで作ってしまいましょう。

スクリプトタスクでの変数の使い方

変数を使用するにはスクリプトタスクのプロパティであるReadOnlyVariables / ReadWriteVariablesに使用する変数を設定する必要があります。

これはまぁ、読んで字の如く読み取り専用と読み書き用で分かれています。

今回の要件であればReadOnlyVariablesにUser::CSVPath、ReadWriteVariablesにUser::CSVPathWithDateを設定してやればいけそうです。

まず変数を取得する方法ですが、Dts.Variables[“変数名”].Valueとすれば取得できます。

public void Main()
{
    var csvPath = Dts.Variables["CSVPath"].Value as string;
    
    var csvWithDate = new StringBuilder(Path.GetDirectoryName(csvPath))
                            .Append(Path.GetFileNameWithoutExtention(csvPath))
                            .AppendFormat("_{0}", DateTime.Now.ToString("yyMMdd_HHmmss"))
                            .AppnedFormat(".{0}", Path.GetExtention(csvPath))
                            .ToString();
    
    // TODO:csvWithDateをSSISの変数にセットする

    Dts.TaskResult = (int)ScriptResults.Success;
}

Valueの型はObjectなのでキャストする必要があります。死んで欲しいですね。

変数に値をセットする場合は同じ要領でValueに値をセットしてやればOKです。

public void Main()
{
    var csvPath = Dts.Variables["CSVPath"].Value as string;
    
    var csvWithDate = new StringBuilder(Path.GetDirectoryName(csvPath))
                            .Append(Path.GetFileNameWithoutExtention(csvPath))
                            .AppendFormat("_{0}", DateTime.Now.ToString("yyMMdd_HHmmss"))
                            .AppnedFormat(".{0}", Path.GetExtention(csvPath))
                            .ToString();
    
    Dts.Variables["CSVWithDate"].Value = csvWithDate;

    Dts.TaskResult = (int)ScriptResults.Success;
}

とまぁ、こんな感じに書き換えることができます。もしUser::CSVPathの拡張子が「.CSV」だったりするとExpressionの方はREPLACEできなくて詰むので、こちらの方が安全でもあります。

まとめ

と言うわけでスクリプトの基礎の基礎でした。今回は単純な例でしたが、もっともっと複雑なことは当然可能です。特にSSISはファイルのI/O周りやログ出力に関する機能が貧弱すぎて全然使えないので、その辺でお世話になることが多いと思われます。

スクリプトコンポーネントでの変数の使い方は次回にまとめて説明します。