0

さて、タイトルが言うように。スクリプトコンポーネントの宛先を使用してから、LINQを使用して出力用に処理する行を選択したいと思います。

もう少し背景として、私はこの醜いものを1対多の関係でマージしました。行は次のようになります。

[ID] [Title]   [OneToManyDataID]
1    Item one   2
1    Item one   4
1    Item one   3
3    Item two   1
3    Item two   5

ID列とTitle列を持つオブジェクトを[Item]と呼び、[OneToMany]と呼びます。

すべてをスクリプトコンポーネントの宛先にスローし、LINQを使用してアイテムごとにグループ化するようなことを行い、最上位のOneToManyオブジェクトからのみデータを取得できることを望んでいました。のようなもの:

foreach(var item  in Data.GroupBy(d=>d.Item).Select(d=> new {Item = d.Key})){
     //Then pick out the highest OneToMany ID for that row to use with it.
}

これを実現するにはおそらくもっと良いLINQクエリがあると思いますが、要点は、SSISのスクリプトコンポーネントでは、事前定義されたProcessInputRowメソッドを使用して行ごとにしか操作できないようです。どの行が処理され、どのプロパティがそのメソッドに渡されるかを正確に判断したいところ。

どうすればこれを行うことができますか?

4

1 に答える 1

5

問題を言い換えると、スクリプトトランスフォーメーションで行ごとの処理を停止させるにはどうすればよいですか?デフォルトでは、スクリプト変換は同期コンポーネントになります-1行入力、1行出力。これを非同期コンポーネントの1行入力-0から多くの行出力に変更する必要があります。

スクリプト変換エディターの[入力と出力]タブで、出力コレクションOutput 0のSynchronousInputIDの値を任意の値からに変更しますNone

私のLINQコードに石を投げかけないでください-それを正しく機能させるためにあなたが処理できると信じています。このコードブロックの目的は、処理のために行を収集し、それらを変更した後、それらをダウンストリームコンシューマーに渡す方法を示すことです。スクリプトコンポーネントのライフサイクルでそれぞれが何をするのかを理解するのに役立つメソッドについてコメントしましたが、MSDNを読みたい場合は、私よりも少し詳しく知っています;)

using System;
using System.Data;
using System.Linq;
using System.Collections.Generic;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
using Microsoft.SqlServer.Dts.Runtime.Wrapper;

[Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute]
public class ScriptMain : UserComponent
{
    /// <summary>
    /// Our LINQ-able thing.
    /// </summary>
    List<Data> data;

    /// <summary>
    /// Do our preexecute tasks, in particular, we will instantiate
    /// our collection.
    /// </summary>
    public override void PreExecute()
    {
        base.PreExecute();
        this.data = new List<Data>();
    }

    /// <summary>
    /// This method is called once the last row has hit.
    /// Since we will can only find the highest OneToManyDataId
    /// after receiving all the rows, this the only time we can
    /// send rows to the output buffer.
    /// </summary>
    public override void FinishOutputs()
    {
        base.FinishOutputs();
        CreateNewOutputRows();
    }

    /// <summary>
    /// Accumulate all the input rows into an internal LINQ-able
    /// collection
    /// </summary>
    /// <param name="Row">The buffer holding the current row</param>
    public override void Input0_ProcessInputRow(Input0Buffer Row)
    {
        // there is probably a more graceful mechanism of spinning
        // up this struct.
        // You must also worry about fields that have null types.
        Data d = new Data();
        d.ID = Row.ID;
        d.Title = Row.Title;
        d.OneToManyId = Row.OneToManyDataID;            
        this.data.Add(d);
    }

    /// <summary>
    /// This is the process to generate new rows. As we only want to
    /// generate rows once all the rows have arrived, only call this
    /// at the point our internal collection has accumulated all the
    /// input rows.
    /// </summary>
    public override void CreateNewOutputRows()
    {
        foreach (var item in this.data.GroupBy(d => d.ID).Select(d => new { Item = d.Key }))
        {
            //Then pick out the highest OneToMany ID for that row to use with it.
            // Magic happens
            // I don't "get" LINQ so I can't implement the poster's action
            int id = 0;
            int maxOneToManyID = 2;
            string title = string.Empty;
            id = item.Item;
            Output0Buffer.AddRow();
            Output0Buffer.ID = id;
            Output0Buffer.OneToManyDataID = maxOneToManyID;
            Output0Buffer.Title = title;
        }
    }

}
/// <summary>
/// I think this works well enough to demo
/// </summary>
public struct Data
{
    public int ID { get; set; }
    public string Title { get; set; }
    public int OneToManyId { get; set; }
}

スクリプト変換の構成

[入力]タブ

出力

結果

于 2012-10-02T14:10:20.513 に答える