スクリプト コンポーネントによる非同期変換の作成
Integration Services パッケージのデータ フロー内で変換コンポーネントを使用することにより、変換元から変換先にデータが受け渡される過程で、データを修正または分析できます。 同期出力型の変換では、各入力列がコンポーネントを通過するときに処理されます。 非同期出力型の変換では、変換が入力行をすべて受け取るまで処理の実行を待機する場合と、変換が入力行をすべて受け取る前に一部の行を出力する場合があります。 このトピックでは、非同期変換について説明します。 処理で同期変換を使用する場合は、「スクリプト コンポーネントによる同期変換の作成」を参照してください。 同期コンポーネントと非同期コンポーネントの相違点の詳細については、「同期および非同期変換について」を参照してください。
スクリプト コンポーネントの概要については、「スクリプト コンポーネントによるデータ フローの拡張」を参照してください。
スクリプト コンポーネントおよびそれによって生成されるインフラストラクチャ コードを活用すれば、カスタム データ フロー コンポーネントを開発するための手順を簡略化できます。 ただし、スクリプト コンポーネントの動作のしくみを理解するため、「カスタム データ フロー コンポーネントの開発」のセクション、特に「同期出力型のカスタム変換コンポーネントの開発」を参照して、カスタム データ フロー コンポーネントの開発手順に目を通しておくと役に立ちます。
非同期変換コンポーネントの概要
SSIS デザイナーの [データ フロー] タブにスクリプト コンポーネントを追加すると、[スクリプト コンポーネントの種類を選択] ダイアログ ボックスが表示され、変換元、変換、または変換先としてあらかじめ設定するコンポーネントの入力を求めるメッセージが表示されます。 このダイアログ ボックスで、[変換] を選択します。
メタデータ デザイン モードでの非同期変換コンポーネントの構成
変換コンポーネントを作成するオプションを選択したら、[スクリプト変換エディター] を使用して、コンポーネントを構成します。 詳細については、「スクリプト コンポーネント エディターでのスクリプト コンポーネントの構成」を参照してください。
スクリプト コンポーネントで使用するスクリプト言語を選択するには、[スクリプト変換エディター] ダイアログ ボックスの [スクリプト] ページにある [ScriptLanguage] プロパティを設定します。
注 |
---|
スクリプト コンポーネントの既定のスクリプト言語を設定するには、[オプション] ダイアログ ボックスの [全般] ページにある [スクリプト言語] オプションを使用します。 詳細については、「[全般] ページ」を参照してください。 |
データ フロー変換コンポーネントには 1 つの入力があり、1 つ以上の出力を設定できます。 コンポーネントの入力および出力の設定は、メタデータ デザイン モードでカスタム スクリプトを記述する前に完了する必要のある作業の 1 つです。これを行うには、[スクリプト変換エディター] を使用します。
入力列の設定
スクリプト コンポーネントを使用して作成した変換コンポーネントには、1 つの入力があります。
[スクリプト変換エディター] の [入力列] ページには、データ フローの上流に位置するコンポーネントの出力で使用可能な列が一覧表示されます。 変換する列、またはそのまま出力する列を選択します。 変換するすべての列の適切な場所に、読み取り/書き込みのマークを付けます。
[スクリプト変換エディター] の [入力列] ページの詳細については、「[スクリプト変換エディター] ([入力列] ページ)」を参照してください。
入力、出力、および出力列の設定
変換コンポーネントには、1 つ以上の出力を設定できます。
通常、非同期出力型の変換には、2 つの出力を設定します。 たとえば、特定の市内にある住所を数えたい場合、1 つの出力に住所データをそのまま送り、もう 1 つの出力に集計結果を送信します。 集計結果を出力するには、新しい出力列も 1 つ必要です。
[スクリプト変換エディター] の [入力および出力] ページには、既定で 1 つの出力が作成されていますが、出力列は作成されていません。 エディターのこのページでは、次の項目を設定できます。
集計結果の出力など、1 つ以上の出力を追加して作成する場合は、 [出力の追加] および [出力の削除] ボタンを使用して、非同期変換コンポーネントの出力を管理します。 各出力の SynchronousInputID プロパティを 0 に設定すると、上流コンポーネントからのデータをそのまま出力するのではなく、変換を実行して、既存の行や列の適切な位置に出力することを示します。 この設定によって、出力が入力に対して非同期になります。
入力や出力にはわかりやすい名前を付けておくことができます。 スクリプト コンポーネントはこの名前を使用して、型指定されたアクセサー プロパティを生成します。これは、スクリプト内で入力や出力を参照するために使用します。
通常、非同期変換は列をデータ フローに追加します。 出力の SynchronousInputID プロパティが 0 の場合、上流コンポーネントからのデータはそのまま出力されず、変換を実行して、既存の行や列の適切な位置に出力されることを示します。この場合、出力に対して出力列を明示的に追加して設定する必要があります。 出力列の名前は、マップ先の入力列と同じ名前にする必要はありません。
追加情報を格納するために列を追加する場合があります。 その場合、追加した列にデータを格納するには、独自のコードを記述する必要があります。 標準エラー出力の動作を再現する方法については、「スクリプト コンポーネントに対するエラー出力のシミュレート」を参照してください。
[スクリプト変換エディター] の [入力および出力] ページの詳細については、「[スクリプト変換エディター] ([入力および出力] ページ)」を参照してください。
変数の追加
既存の変数の値をスクリプト内で使用する場合、[スクリプト変換エディター] の [スクリプト] ページで、ReadOnlyVariables プロパティおよび ReadWriteVariables プロパティのフィールドに追加できます。
プロパティ フィールドに複数の変数を追加する場合は、各変数名をコンマで区切ります。 また、ReadOnlyVariables プロパティ フィールドや ReadWriteVariables プロパティ フィールドの横にある参照ボタン ([...]) をクリックして [変数の選択] ダイアログ ボックスを開き、複数の変数を選択することもできます。
スクリプト コンポーネントで変数を使用する方法に関する一般情報については、「スクリプト コンポーネントでの変数の使用」を参照してください。
[スクリプト変換エディター] の [スクリプト] ページの詳細については、「[スクリプト変換エディター] ([スクリプト] ページ)」を参照してください。
コード デザイン モードでの非同期変換コンポーネントのスクリプト作成
コンポーネントのメタデータをすべて構成した後、カスタム スクリプトを記述できます。 [スクリプト変換エディター] の [スクリプト] ページで [スクリプトの編集] をクリックし、Microsoft Visual Studio Tools for Applications (VSTA) IDE を開いて、カスタム スクリプトを追加できます。 使用するスクリプト言語は、[スクリプト] ページの [ScriptLanguage] プロパティで、Microsoft Visual Basic と Microsoft Visual C# のどちらをスクリプト言語として選択したかによって決まります。
スクリプト コンポーネントを使用して作成するすべての種類のコンポーネントに関する重要情報については、「スクリプト コンポーネントのコーディングおよびデバッグ」を参照してください。
自動生成されたコードについて
変換コンポーネントを作成および構成した後で VSTA IDE を開くと、コード エディターには ScriptMain クラスが編集可能な状態で表示されます。また、ProcessInputRow および CreateNewOutputRows メソッドがスタブとして表示されます。 カスタム コードは ScriptMain クラスに記述します。また、ProcessInputRow は変換コンポーネントの最重要メソッドです。 CreateNewOutputRows メソッドは変換元コンポーネントで一般的に使用され、両方のコンポーネントが独自の出力行を作成する必要のある、非同期変換と似ています。
VSTA の [プロジェクト エクスプローラー] ウィンドウを開くと、スクリプト コンポーネントにより、BufferWrapper および ComponentWrapper というプロジェクト アイテムが、読み取り専用の状態で自動生成されていることもわかります。 ScriptMain クラスは、ComponentWrapper プロジェクト アイテム内の UserComponent クラスを継承します。
実行時には、データ フロー エンジンが UserComponent クラスの PrimeOutput メソッドを呼び出します。これは親クラスである ScriptComponent の PrimeOutput メソッドをオーバーライドします。 次に、PrimeOutput メソッドは CreateNewOutputRows メソッドを呼び出します。
その後、データ フロー エンジンが UserComponent クラスの ProcessInput メソッドを呼び出します。これは親クラスである ScriptComponent の ProcessInput メソッドをオーバーライドします。 ProcessInput メソッドは、入力バッファーに格納された行を順にループし、各行で 1 回ずつ ProcessInputRow メソッドを呼び出します。
カスタム コードの記述
非同期型のカスタム変換コンポーネントの作成を完了するには、オーバーライドされた ProcessInputRow メソッドを使用して、入力バッファーにある各行のデータを処理します。 出力は入力に同期しないため、データの行を明示的に出力に書き込む必要があります。
非同期変換では、AddRow メソッドを使用して、ProcessInputRow メソッドまたは ProcessInput メソッド内から必要に応じて行を出力に追加できます。 CreateNewOutputRows メソッドを使用する必要はありません。 集計結果などの結果の単一行を特定の出力に書き込んでいる場合は、CreateNewOutputRows メソッドを使用してあらかじめ出力行を作成し、その値をすべての入力行を処理した後に入力できます。 ただし、スクリプト コンポーネントでは入力または出力で現在の行のみを使用できるため、CreateNewOutputRows メソッドで複数の行を作成しても役に立ちません。 CreateNewOutputRows メソッドは、処理する入力行のない変換元コンポーネントでより重要になります。
また、ProcessInput メソッド自体をオーバーライドし、入力バッファー内をループして各行で ProcessInputRow を呼び出す前後に、準備処理や終了処理を追加することもできます。 たとえば、このトピックのコード例の 1 つでは、ProcessInput メソッドをオーバーライドして、各行をループするたびに ProcessInputRow メソッドを呼び出し、特定の市内にある住所を数えています。この例では、すべての行について処理が終わると、集計値が 2 番目の出力に書き込まれます。 この例で出力の終了処理が ProcessInput で行われているのは、PostExecute が呼び出される時点では、出力バッファーが既に使用できなくなっているためです。
必要に応じて、ScriptMain クラスで使用できる PreExecute メソッドや PostExecute メソッドにもスクリプトを記述して、準備処理や終了処理を行う場合もあります。
注 |
---|
カスタム データ フロー コンポーネントを最初から開発した場合、PrimeOutput メソッドをオーバーライドして、出力バッファーに対する参照をキャッシュに保存し、後でデータの行をバッファーに追加できるようにする必要があります。 スクリプト コンポーネントを使用する場合は、オーバーライドする必要はありません。BufferWrapper プロジェクト アイテム内に、各出力バッファーを表すクラスが自動生成されるためです。 |
例
次の例では、非同期変換コンポーネントを作成するために ScriptMain クラスに記述する必要のあるカスタム コードを示します。
注 |
---|
これらの例では、サンプル データベース AdventureWorks の Person.Address テーブルを使用して、その第 1 列および第 4 列、つまり int 型の AddressID および nvarchar(30) 型の City の各列を、データ フローにそのまま渡します。 このセクションの変換元、変換、および変換先の例でも、同じデータが使用されます。 他の前提条件および仮定条件については、それぞれの例で説明します。 |
次の例では、2 つの出力を持つ非同期変換コンポーネントを示します。 この変換では、AddressID 列および City 列のデータを 1 つの出力にそのまま渡し、特定の都市 (Redmond, Washington, U.S.A.) 内にある住所を数え、2 番目の出力にその結果を送ります。
このサンプル コードを実行する場合は、パッケージやコンポーネントを次のように設定する必要があります。
新しいスクリプト コンポーネントを [データ フロー] デザイナー画面に追加し、変換として構成します。
デザイナーで、変換元または他の変換の出力を、新しい変換コンポーネントに接続します。 この出力には、サンプル データベース AdventureWorks の Person.Address テーブルから、少なくとも AddressID 列および City 列を含むデータが供給される必要があります。
[スクリプト変換エディター] を開きます。 [入力列] ページで、AddressID 列と City 列を選択します。
[入力および出力] ページで、最初の出力の AddressID 出力列と City 出力列を追加および構成します。 2 番目の出力を追加し、集計値を格納する出力列をその出力に追加します。 この例により各入力行が最初の出力に明示的にコピーされるため、最初の出力の SynchronousInputID プロパティを 0 に設定します。 新しく作成された出力の SynchronousInputID プロパティは既に 0 に設定されています。
入力、出力、および新しい出力列をわかりやすい名前に変更します。 この例では、入力の名前を MyAddressInput、出力の名前を MyAddressOutput および MySummaryOutput とし、2 番目の出力の列名を MyRedmondCount としています。
[スクリプト] ページで、[スクリプトの編集] をクリックし、続きのスクリプトを入力します。 次に、スクリプト開発環境と [スクリプト変換エディター] を閉じます。
SQL Server の変換先、または「スクリプト コンポーネントによる変換先の作成」で説明されている変換先コンポーネントの例など、AddressID および City 列が含まれる最初の出力の変換先コンポーネントを作成して構成します。 次に、変換の最初の出力である MyAddressOutput を変換先コンポーネントに接続します。 AdventureWorks データベースで次の Transact-SQL コマンドを実行して、変換先テーブルを作成できます。
CREATE TABLE [Person].[Address2]( [AddressID] [int] NOT NULL, [City] [nvarchar](30) NOT NULL )
2 番目の出力に接続する別の変換先コンポーネントを作成して構成します。 次に、変換の 2 番目の出力である MySummaryOutput を変換先コンポーネントに接続します。 2 番目の出力からは 1 つの値を格納した行が 1 つ出力されるだけなので、フラット ファイル接続マネージャーを使用して、1 つの列を含む新しいファイルに接続する設定を簡単に行うことができます。 この例では、この変換先の列に MyRedmondCount という名前を付けます。
サンプルを実行します。
Public Class ScriptMain
Inherits UserComponent
Private myRedmondAddressCount As Integer
Public Overrides Sub CreateNewOutputRows()
MySummaryOutputBuffer.AddRow()
End Sub
Public Overrides Sub MyAddressInput_ProcessInput(ByVal Buffer As MyAddressInputBuffer)
While Buffer.NextRow()
MyAddressInput_ProcessInputRow(Buffer)
End While
If Buffer.EndOfRowset Then
MyAddressOutputBuffer.SetEndOfRowset()
MySummaryOutputBuffer.MyRedmondCount = myRedmondAddressCount
MySummaryOutputBuffer.SetEndOfRowset()
End If
End Sub
Public Overrides Sub MyAddressInput_ProcessInputRow(ByVal Row As MyAddressInputBuffer)
With MyAddressOutputBuffer
.AddRow()
.AddressID = Row.AddressID
.City = Row.City
End With
If Row.City.ToUpper = "REDMOND" Then
myRedmondAddressCount += 1
End If
End Sub
End Class
public class ScriptMain:
UserComponent
{
private int myRedmondAddressCount;
public override void CreateNewOutputRows()
{
MySummaryOutputBuffer.AddRow();
}
public override void MyAddressInput_ProcessInput(MyAddressInputBuffer Buffer)
{
while (Buffer.NextRow())
{
MyAddressInput_ProcessInputRow(Buffer);
}
if (Buffer.EndOfRowset())
{
MyAddressOutputBuffer.SetEndOfRowset();
MySummaryOutputBuffer.MyRedmondCount = myRedmondAddressCount;
MySummaryOutputBuffer.SetEndOfRowset();
}
}
public override void MyAddressInput_ProcessInputRow(MyAddressInputBuffer Row)
{
{
MyAddressOutputBuffer.AddRow();
MyAddressOutputBuffer.AddressID = Row.AddressID;
MyAddressOutputBuffer.City = Row.City;
}
if (Row.City.ToUpper() == "REDMOND")
{
myRedmondAddressCount += 1;
}
}
}
|