Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/typo3/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql server SSIS自定义数据流组件循环输入管道缓冲区多次_Sql Server_Ssis - Fatal编程技术网

Sql server SSIS自定义数据流组件循环输入管道缓冲区多次

Sql server SSIS自定义数据流组件循环输入管道缓冲区多次,sql-server,ssis,Sql Server,Ssis,我仍在学习如何在SSIS中创建自定义组件。假设我有一个500行的输入行计数,我需要从输入管道缓冲区一次读取/批处理/处理100行,然后将它们发送给第三方应用程序,一旦得到结果,我需要用新数据更新管道缓冲区列,然后读取/批处理/处理接下来的100行,依此类推,直到我处理完所有500行 我的问题是,我是否可以多次循环/读取输入管道缓冲区,以便使用第三方应用程序返回的数据更新缓冲区 我想我读到了可以读入所有数据并将其存储到缓存中,然后对数据进行排序,但我不确定如何将数据从缓存返回到输出。我也不确定应该

我仍在学习如何在SSIS中创建自定义组件。假设我有一个500行的输入行计数,我需要从输入管道缓冲区一次读取/批处理/处理100行,然后将它们发送给第三方应用程序,一旦得到结果,我需要用新数据更新管道缓冲区列,然后读取/批处理/处理接下来的100行,依此类推,直到我处理完所有500行

我的问题是,我是否可以多次循环/读取输入管道缓冲区,以便使用第三方应用程序返回的数据更新缓冲区

我想我读到了可以读入所有数据并将其存储到缓存中,然后对数据进行排序,但我不确定如何将数据从缓存返回到输出。我也不确定应该在哪里做,以及如何访问输入管道缓冲区、PrimeOutput或ProcessInput或其他我不知道的重写方法

我试图创建一个定制的异步数据流组件来解决这个问题

任何帮助或想法都将不胜感激,并/或为我指明正确的方向


谢谢

我很高兴我没有尝试徒手写这篇文章,因为我忘记了很多好的观点

这里有几点值得注意:我的两个数据结构
InData
OutData
,您需要对它们进行配置,以跟踪输入/输出缓冲区中的内容。正如注释所述,可能有一种聪明的方法可以克隆缓冲区对象的属性,但我不知道如何克隆。定义这些列以匹配数据流中的数据类型,如果您像我一样懒惰,请使用相同的列名,您可以复制/粘贴您的成功之路

ApiCall
是一个虚拟方法,它使用缓存的值来请求数据清理服务完成它的工作。它需要返回清理后的数据,以便我们可以将输入和输出合并到一个统一的行中。可能有更好的方法可以做到这一点,但希望它足以激发你的思维过程

我创建了一个SSIS级别变量,
@[User::ApiBatchSize]
,您可以将其初始化为500。使用此方法将允许您在不更改核心代码的情况下优化发送的批大小。我在
PreExecute
方法中初始化本地成员变量,因为这是脚本组件的构造函数

通常,在异步脚本组件中,您使用的是
ProcessInputRow
方法,这是我最初使用的方法,但如果列表的大小是APIBACHTSIZE的偶数倍,则最终批处理会遇到问题。结果是,在该方法中,
EndOfRowset()
从未设置为True。不用担心,我们只需要使用
ProcessInput
方法。在“正常”情况下,process input方法会导致process input行处理一行,因此我们将跳过中间人,直接使用process input中的缓冲区。我很懒,没有将我的
引用重命名为
缓冲区
,因为自动生成的代码最初处理了参数

这里的伪逻辑是

  • 当有数据行时
    • 如果我们达到了批量大小,请发送数据收集以进行处理
      • 对于每个已处理的行,向输出缓冲区添加一行,并用干净的数据填充它
    • 清空我们的收集桶(已经发送到下游)
  • 将当前行添加到我们的收集桶中
C#本身

使用系统;
使用系统数据;
使用System.Collections.Generic;
使用Microsoft.SqlServer.Dts.Pipeline.Wrapper;
使用Microsoft.SqlServer.Dts.Runtime.Wrapper;
/// 
///有一种聪明的方法可以重用输入/输出缓冲区中的元数据
///定义,但我不知道如何访问它,所以我在这里重新定义它
/// 
公共结构InData
{
公共字符串AddressLine1{get;set;}
}
/// 
///有一种聪明的方法可以重用输入/输出缓冲区中的元数据
///定义,但我不知道如何访问它,所以我在这里重新定义它
/// 
公共结构输出数据
{
公共字符串AddressLine1Clean{get;set;}
公共字符串AddressCityClean{get;set;}
公共字符串AddressStateClean{get;set;}
公共字符串地址PostalCodeClean{get;set;}
}
/// 
///这是要向其中添加代码的类。不要更改名称、属性或父级
///这个班的学生。
/// 
[Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute]
公共类ScriptMain:UserComponent
{
列出mData;
国际工商管理学院;
/// 
///在数据流中开始处理行之前,调用此方法一次。
///
///如果不需要在此处执行任何操作,可以删除此方法。
/// 
公共覆盖无效预执行()
{
base.PreExecute();
this.mData=新列表();
this.mBatchSize=this.Variables.ApiBatchSize;
}
/// 
///此方法在所有行都通过此组件后调用。
///
///如果不需要在此处执行任何操作,可以删除此方法。
/// 
公共重写void PostExecute()
{
base.PostExecute();
}
/// 
///我们将使用ProcessInput,而不是ProcessInputRow
///“更接近裸金属”,我们需要它
/// 
/// 
公共覆盖无效Input0\u ProcessInput(Input0Buffer行)
{
//base.Input0\u ProcessInput(缓冲区);
while(Row.NextRow())
{
如果(this.mData.Count>=this.mBatchSize)
{
foreach(ApiCall()中的变量项)
{
Output0Buffer.AddRow();
var inRow=项。键;
var outRow=项目价值;
//f
using System;
using System.Data;
using System.Collections.Generic;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
using Microsoft.SqlServer.Dts.Runtime.Wrapper;

/// <summary>
/// There might be a clever way to re-use the metadata from the Input/OutputBuffer 
/// definition but  I don't know how to access it so I redefine it here
/// </summary>
public struct InData
{
    public string AddressLine1 { get; set; }
}

/// <summary>
/// There might be a clever way to re-use the metadata from the Input/OutputBuffer 
/// definition but  I don't know how to access it so I redefine it here
/// </summary>
public struct OutData
{
    public string AddressLine1Clean { get; set; }
    public string AddressCityClean { get; set; }
    public string AddressStateClean { get; set; }
    public string AddressPostalCodeClean { get; set; }
}

/// <summary>
/// This is the class to which to add your code.  Do not change the name, attributes, or parent
/// of this class.
/// </summary>
[Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute]
public class ScriptMain : UserComponent
{
    List<InData> mData;
    int mBatchSize;

    /// <summary>
    /// This method is called once, before rows begin to be processed in the data flow.
    ///
    /// You can remove this method if you don't need to do anything here.
    /// </summary>
    public override void PreExecute()
    {
        base.PreExecute();

        this.mData = new List<InData>();
        this.mBatchSize = this.Variables.ApiBatchSize;
    }

    /// <summary>
    /// This method is called after all the rows have passed through this component.
    ///
    /// You can delete this method if you don't need to do anything here.
    /// </summary>
    public override void PostExecute()
    {
        base.PostExecute();

    }

    /// <summary>
    /// We're going to work with ProcessInput versus PorcessInputRow as it is
    /// "closer to the bare metal" and we need that
    /// </summary>
    /// <param name="Buffer"></param>
    public override void Input0_ProcessInput(Input0Buffer Row)
    {
        //base.Input0_ProcessInput(Buffer);

        while (Row.NextRow())
        {
            if (this.mData.Count >= this.mBatchSize)
            {
                foreach (var item in ApiCall())
                {
                    Output0Buffer.AddRow();
                    var inRow = item.Key;
                    var outRow = item.Value;

                    // fill columns with original data
                    Output0Buffer.AddressLine1 = inRow.AddressLine1;
                    // etc

                    // fill columns with clean data
                    Output0Buffer.AddressLine1Clean = outRow.AddressLine1Clean;
                    Output0Buffer.AddressCityClean = outRow.AddressCityClean;
                    Output0Buffer.AddressStateClean = outRow.AddressStateClean;
                    Output0Buffer.AddressPostalCodeClean = outRow.AddressPostalCodeClean;
                    // etc
                }

                // TODO Remove this for production, just ensuring batching is working as intended
                bool fireAgain = false;
                string status = "Batch released. Conditions => mDataCount := " + this.mData.Count;
                this.ComponentMetaData.FireInformation(0, "ApiProcessing", status, "", 0, ref fireAgain);

                // Reset for next iteration
                this.mData.Clear();
            }

            this.mData.Add(new InData() { AddressLine1 = Row.AddressLine1 });
        }

        // Handle the final possible partial batch
        if (this.mData.Count > 0)
        {
            foreach (var item in ApiCall())
            {
                Output0Buffer.AddRow();
                var inRow = item.Key;
                var outRow = item.Value;

                // fill columns with original data
                Output0Buffer.AddressLine1 = inRow.AddressLine1;
                // etc

                // fill columns with clean data
                Output0Buffer.AddressLine1Clean = outRow.AddressLine1Clean;
                Output0Buffer.AddressCityClean = outRow.AddressCityClean;
                Output0Buffer.AddressStateClean = outRow.AddressStateClean;
                Output0Buffer.AddressPostalCodeClean = outRow.AddressPostalCodeClean;
                // etc
            }

            // TODO Remove this for production, just ensuring batching is working as intended
            bool fireAgain = false;
            string status = "Final batch released. Conditions => mDataCount := " + this.mData.Count;
            this.ComponentMetaData.FireInformation(0, "ApiProcessing", status, "", 0, ref fireAgain);

            // Reset for next iteration
            this.mData.Clear();

        }
    }

    ///// <summary>
    ///// This method is called once for every row that passes through the component from Input0.
    ///// We need to preserve rows in our own memory allocation
    ///// We're not getting the EndOfRowset call in time to release the final
    ///// </summary>
    ///// <param name="Row">The row that is currently passing through the component</param>
    //public override void Input0_ProcessInputRow(Input0Buffer Row)
    //{
    //}

    public override void CreateNewOutputRows()
    {
        // I don't think we need to do anything special here
        // but I'm leaving it in in case you have some weird case
    }

    /// <summary>
    /// Simulate data cleaning
    /// </summary>
    /// <returns></returns>
    public Dictionary<InData, OutData> ApiCall()
    {
        int macGuffin = 0;
        Dictionary<InData, OutData> cleanData = new Dictionary<InData, OutData>();
        foreach (var item in this.mData)
        {
            cleanData.Add(item, new OutData() { AddressLine1Clean = "Clean" + item.AddressLine1, AddressCityClean = "Clean", AddressPostalCodeClean = "12345-1234", AddressStateClean = "CL"  });
            macGuffin = macGuffin % this.mBatchSize;
        }

        return cleanData;
    }

}